Skip to content

Commit

Permalink
Unified formatting of historical and live flows
Browse files Browse the repository at this point in the history
  • Loading branch information
lucaderi committed Aug 9, 2024
1 parent dbfa98b commit d787367
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 99 deletions.
53 changes: 26 additions & 27 deletions scripts/lua/flow_details.lua
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,7 @@ else
historicalProtoHostHref(ifid, flow["cli.ip"], flow["proto.l4"], flow["proto.ndpi_id"],
page_utils.safe_html(flow["protos.tls.certificate"] or ''))
end

if ((flow["protos.tls_version"] ~= nil) and (flow["protos.tls_version"] ~= 0)) then
local tls_version_name = ntop.getTLSVersionName(flow["protos.tls_version"])

Expand Down Expand Up @@ -1129,7 +1129,7 @@ else
if (not(isEmptyString(flow["protos.tls.client_requested_server_name"]))) then
print("<tr><th width=10%><i class='fas fa-lock'></i> " .. i18n("flow_details.tls_certificate") .. "</th><td>")
print(i18n("flow_details.client_requested") .. ":<br>")

-- TLS, so use https
print(format_external_link(page_utils.safe_html(flow["protos.tls.client_requested_server_name"]),
page_utils.safe_html(flow["protos.tls.client_requested_server_name"]), false, "https"))
Expand All @@ -1140,9 +1140,9 @@ else
page_utils.safe_html(flow["protos.tls.client_requested_server_name"] or ''), flow["vlan"])
printAddCustomHostRule(flow["protos.tls.client_requested_server_name"])
print("</td>")

print("<td>")

if (flow["protos.tls.server_names"] ~= nil) then
local servers = string.split(flow["protos.tls.server_names"], ",") or {flow["protos.tls.server_names"]}

Expand Down Expand Up @@ -1429,46 +1429,45 @@ else
print("<table class=\"table table-bordered table-striped\" width=100%>\n")

print("<tr><th>" .. i18n("description") .. "</th><th>" .. i18n("score") .. "</th><th>" .. i18n("info") .. "</th><th>".. i18n("mitre_id") .. "</th><th>".. i18n("remediation") .. "</th><th>" .. i18n("actions") .. "</th></tr>\n")

for _, score_alerts in pairsByKeys(alerts_by_score, rev) do
for _, score_alert in pairsByField(score_alerts, "message", asc) do
local alert_key = nil
local mitre_info = nil

local status_icon = ""

local riskLabel = riskInfo[tostring(score_alert.alert_risk)]
local status_icon = ""
local riskLabel = riskInfo[tostring(score_alert.alert_risk)]

if (riskLabel ~= nil) then
riskLabel = shortenString(riskLabel, 64)
else
riskLabel = ""
end
if (riskLabel ~= nil) then
riskLabel = shortenString(riskLabel, 64)
else
riskLabel = ""
end

if score_alert.alert_id then
alert_consts.alertTypeIcon(score_alert.alert_id, map_score_to_severity(score_alert.alert_id),
'fa-lg')
if score_alert.alert_id then
alert_consts.alertTypeIcon(score_alert.alert_id, map_score_to_severity(score_alert.alert_id), 'fa-lg')
end

local alert_source = " <span class='badge bg-info'>".. ternary(score_alert.alert_risk, "nDPI", "ntopng") .. "</span>"

print(string.format('<tr>'))

if score_alert.alert_id then
alert_key = alert_consts.getAlertType(tonumber(score_alert.alert_id), alert_entities.flow.entity_id)

if(alert_key ~= nil) then
mitre_info = alert_consts.getAlertMitreInfo(alert_key)
mitre_info = alert_consts.getAlertMitreInfo(alert_key)
end
end

local additional = ""
local severity = alert_consts.alertSeverityById(map_score_to_severity(score_alert.score))

local msg = string.format('<td> %s </td><td style=\"text-align: center;\"> %s </td><td> %s %s %s</td>',
score_alert.message .. alert_source, score_alert.score,
riskLabel, (score_alert.alert_risk > 0 and flow_risk_utils.get_documentation_link(score_alert.alert_risk)) or '',
score_alert.message .. alert_source,
'<span style="color:' .. severity.color .. '">' .. score_alert.score .. '</span>',
riskLabel, (score_alert.alert_risk > 0 and flow_risk_utils.get_documentation_link(score_alert.alert_risk)) or '',
status_icon or '')
print(msg)

if score_alert.alert_id then
Expand All @@ -1478,18 +1477,18 @@ else
-- tprint(mitre_info)

additional = "<br>"..i18n(mitre_info.mitre_tactic['i18n_label'])

if(mitre_info.mitre_sub_technique ~= nil) then
-- additional = additional .."<br>" .. i18n(mitre_info.mitre_sub_technique.i18n_label)
end

local keys = split(mitre_info.mitre_id, "%.")
local url = "https://attack.mitre.org/techniques/"..keys[1]:gsub("%%", "").."/"

if(keys[2] ~= nil) then
url = url .. keys[2]:gsub("%%", "") .. "/"
end

print('<A HREF="'..url..'">'..mitre_info.mitre_id.."</A>"..additional)
else
print("&nbsp;")
Expand All @@ -1498,9 +1497,9 @@ else
print('<td style=\"text-align: center;\">'..
flow_risk_utils.get_remediation_documentation_link(score_alert.alert_id)
.. '</td>')

print('<td nowrap>')

-- Add rules to disable the check
print(string.format(
'<a href="#alerts_filter_dialog" alert_id=%u alert_label="%s" class="btn btn-sm btn-warning" role="button"><i class="fas fa-bell-slash"></i></a>',
Expand Down
106 changes: 53 additions & 53 deletions scripts/lua/modules/alert_utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -383,61 +383,61 @@ end
-- #################################

function alert_utils.formatFlowAlertMessage(ifid, alert, alert_json, add_score, local_explorer)
local msg
local alert_risk

if tonumber(alert.alert_id) then
alert_risk = ntop.getFlowAlertRisk(tonumber(alert.alert_id))
end

if not alert_json then
alert_json = alert_utils.getAlertInfo(alert)
end

local description = alertTypeDescription(alert.alert_id, alert_entities.flow.entity_id)

if (type(description) == "string") then
-- localization string
msg = i18n(description, alert_json)
elseif (type(description) == "function") then
msg = description(ifid, alert, alert_json, local_explorer)
end

if isEmptyString(msg) then
if alert_json and alert_json.alert_generation and alert_risk and alert_risk > 0 then
-- Flow risks most of the times already have a default description, use this in case of emtpy descr
msg = alert_utils.get_flow_risk_info(alert_risk, alert_json)
else
-- Normal alerts
msg = alert_consts.alertTypeLabel(tonumber(alert.alert_id), true --[[ no_html --]] , alert.entity_id)
end
end

if not isEmptyString(alert["user_label"]) then
msg = string.format('%s <small><span class="text-muted">%s</span></small>', msg, alert["user_label"])
end

if add_score then
if tonumber(alert.alert_id) then
local alert_score = ntop.getFlowAlertScore(tonumber(alert.alert_id))
msg = alert_utils.format_score(msg, alert_score)
end
end

-- Add the link to the documentation
if alert_risk and alert_risk > 0 then
msg = string.format("%s %s %s",
msg, flow_risk_utils.get_documentation_link(alert_risk),
flow_risk_utils.get_remediation_documentation_link(alert.alert_id))
local info_msg = alert_utils.get_flow_risk_info(alert_risk, alert_json)

-- Add check info_msg ~= alert.info to avoid duplicated in description msg
--[[if (not isEmptyString(info_msg) and info_msg ~= alert.info) then
local msg
local alert_risk

if tonumber(alert.alert_id) then
alert_risk = ntop.getFlowAlertRisk(tonumber(alert.alert_id))
end

if not alert_json then
alert_json = alert_utils.getAlertInfo(alert)
end

local description = alertTypeDescription(alert.alert_id, alert_entities.flow.entity_id)

if (type(description) == "string") then
-- localization string
msg = i18n(description, alert_json)
elseif (type(description) == "function") then
msg = description(ifid, alert, alert_json, local_explorer)
end

if isEmptyString(msg) then
if alert_json and alert_json.alert_generation and alert_risk and alert_risk > 0 then
-- Flow risks most of the times already have a default description, use this in case of emtpy descr
msg = alert_utils.get_flow_risk_info(alert_risk, alert_json)
else
-- Normal alerts
msg = alert_consts.alertTypeLabel(tonumber(alert.alert_id), true --[[ no_html --]] , alert.entity_id)
end
end

if not isEmptyString(alert["user_label"]) then
msg = string.format('%s <small><span class="text-muted">%s</span></small>', msg, alert["user_label"])
end

if add_score then
if tonumber(alert.alert_id) then
local alert_score = ntop.getFlowAlertScore(tonumber(alert.alert_id))
msg = alert_utils.format_score(msg, alert_score)
end
end

-- Add the link to the documentation
if alert_risk and alert_risk > 0 then
msg = string.format("%s %s %s",
msg, flow_risk_utils.get_documentation_link(alert_risk),
flow_risk_utils.get_remediation_documentation_link(alert.alert_id))
local info_msg = alert_utils.get_flow_risk_info(alert_risk, alert_json)

-- Add check info_msg ~= alert.info to avoid duplicated in description msg
--[[if (not isEmptyString(info_msg) and info_msg ~= alert.info) then
msg = string.format("%s", msg, info_msg)
end--]]
end
end--]]
end

return msg or ""
return msg or ""
end

-- #################################
Expand Down
66 changes: 47 additions & 19 deletions scripts/lua/modules/historical_flow_details_formatter.lua
Original file line number Diff line number Diff line change
Expand Up @@ -208,9 +208,9 @@ local function format_historical_host_pool(flow, info)
}
end

-- ###############################################
-- a###############################################

local function format_historical_issue_description(alert_id, score, title, msg, alert_scores)
local function format_historical_issue_description(alert_id, score, title, msg, info, alert_scores)
local alert_consts = require "alert_consts"
local alert_entities = require "alert_entities"

Expand All @@ -221,10 +221,13 @@ local function format_historical_issue_description(alert_id, score, title, msg,
if alert_scores and alert_scores[alert_id] then
score = alert_scores[alert_id]
end

local severity_id = map_score_to_severity(score)
local severity = alert_consts.alertSeverityById(severity_id)

local value = i18n('score') .. ': <span style="color:' .. severity.color .. '">' .. score .. '</span> - ' .. msg
-- local alert_source = " <span class='badge bg-info'>".. ternary(score_alert.alert_risk, "nDPI", "ntopng") .. "</span>"

local html = "<tr><td>"..(msg or "").."</td>"..'<td align=center><span style="color:' .. severity.color .. '">' .. score .. '</span></td>'
html = html .. "<td>" .. info .. "</td>"

<<<<<<< HEAD
-- Add Mitre info
Expand All @@ -240,9 +243,12 @@ local function format_historical_issue_description(alert_id, score, title, msg,
local mitre_info = alert_consts.getAlertMitreInfo(alert_key)

if mitre_info then
<<<<<<< HEAD
value = value .. ' - ' .. i18n("mitre_id") .. ' '
>>>>>>> 0c05864da (Fixed invalid historical flow formatting)

=======
>>>>>>> e9eaec7cf (Unified formatting of historical and live flows)
local keys = split(mitre_info.mitre_id, "%.")
local url = "https://attack.mitre.org/techniques/"..keys[1]:gsub("%%", "").."/"

Expand All @@ -256,17 +262,22 @@ local function format_historical_issue_description(alert_id, score, title, msg,
if keys[2] ~= nil then
url = url .. keys[2]:gsub("%%", "") .. "/"
end
<<<<<<< HEAD
value = value .. '<a href="'..url..'">'..mitre_info.mitre_id.."</A>"
>>>>>>> 0c05864da (Fixed invalid historical flow formatting)
=======
>>>>>>> e9eaec7cf (Unified formatting of historical and live flows)

value = value .. ' ' .. (i18n(mitre_info.mitre_tactic.i18n_label))
html = html .. '<td><a href="'..url..'">'..mitre_info.mitre_id.."</A>"
html = html .. '<br>' .. i18n(mitre_info.mitre_tactic.i18n_label) .. "</td>"
else
html = html .. "<td>&nbsp;</td>"
end
else
html = html .. "<td>&nbsp;</td>"
end

return {
name = title,
values = { value }
}
return html
end

-- ###############################################
Expand All @@ -281,14 +292,15 @@ local function format_historical_issues(flow_details, flow)
local alert_json = json.decode(flow["ALERT_JSON"] or '') or {}
local details = ""
local alert

local alert_store_instance = alert_store_instances[alert_entities["flow"].alert_store_name]

if alert_store_instance then
local alerts, _ = alert_store_instance:select_request(nil, "*")
if alerts and #alerts >= 1 then
alert = alerts[1]
details = alert_utils.formatFlowAlertMessage(interface.getId(), alert, alert_json, false, true)
tprint(details)
end
end

Expand All @@ -298,6 +310,7 @@ local function format_historical_issues(flow_details, flow)
local alert_label = i18n("flow_details.normal")
local alert_id = tonumber(flow["STATUS"] or 0)
local main_alert_score = ntop.getFlowAlertScore(tonumber(alert_id))

-- Check if there is a custom score
if alert_scores and alert_scores[tostring(alert_id)] then
main_alert_score = alert_scores[tostring(alert_id)]
Expand All @@ -307,29 +320,44 @@ local function format_historical_issues(flow_details, flow)

flow_details[#flow_details + 1] = {
name = i18n('total_flow_score'),
values = {'<span style="color:' .. severity.color .. '">' .. format_utils.formatValue(tonumber(flow["SCORE"])) ..
'</span>', ''}
values = {'<span style="color:' .. severity.color .. '">' .. format_utils.formatValue(tonumber(flow["SCORE"])) .. '</span>', ''}
}

local html = ""

-- No status set
if (alert_id ~= 0) then

alert_label = alert_consts.alertTypeLabel(alert_id, true)
if not isEmptyString(details) then
alert_label = alert_label .. " [" .. details .. "]"
end

flow_details[#flow_details + 1] = format_historical_issue_description(tostring(alert_id), tonumber(main_alert_score), i18n("issues_score"), alert_label, alert_scores)
html = "<table class=\"table table-bordered table-striped\" width=100%>\n"
html = html .. "<tr><th>" .. i18n("description") .. "</th><th>" .. i18n("score") .. "</th><th>".. i18n("info") .. " / ".. i18n("remediation").. "</th><th>".. i18n("mitre_id") .. "</th></tr>\n"
html = html .. format_historical_issue_description(tostring(alert_id), tonumber(main_alert_score), i18n("issues_score"), alert_label, details, alert_scores)
end

local alert_utils = require "alert_utils"
local _, other_issues = alert_utils.format_other_alerts(flow['ALERTS_MAP'], flow['STATUS'], alert_json, false, nil,
true)
local _, other_issues = alert_utils.format_other_alerts(flow['ALERTS_MAP'], flow['STATUS'], alert_json, false, nil, true)

if table.len(other_issues) > 0 then
for _, issue in pairs(other_issues or {}) do
flow_details[#flow_details + 1] = format_historical_issue_description(tostring(issue.alert_id), tonumber(issue.score), '', issue.msg, alert_scores)
tprint(issue.msg)
local msg, info
local pieces = string.split(issue.msg, "%[")

if(pieces ~= nil) then
msg = pieces[1]
info = string.gsub(pieces[2], "%]", "")
else
msg = issue.msg
info = ""
end
html = html .. format_historical_issue_description(tostring(issue.alert_id), tonumber(issue.score), '', msg, info, alert_scores)
end
end

flow_details[#flow_details + 1] = { name = i18n('total_flow_score'), values = { html } }


return flow_details
end

Expand Down

0 comments on commit d787367

Please sign in to comment.