1
# Copyright (C) 2004,2005,2006 PreludeIDS Technologies. All Rights Reserved.
2
# Author: Nicolas Delon <nicolas.delon@prelude-ids.com>
4
# This file is part of the Prewikka program.
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2, or (at your option)
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
# GNU General Public License for more details.
16
# You should have received a copy of the GNU General Public License
17
# along with this program; see the file COPYING. If not, write to
18
# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
20
import copy, re, urllib, time, prelude, preludedb, operator
21
from prewikka import view, User, utils
22
from prewikka.views.messagelisting import MessageListingParameters, MessageListing, ListedMessage
25
def cmp_severities(x, y):
26
d = { None: 0, "info": 1, "low": 2, "medium": 3, "high": 4 }
30
def _normalizeName(name):
31
return "".join([ i.capitalize() for i in name.split("_") ])
34
def _getEnumValue(class_id):
39
value = prelude.idmef_class_enum_to_string(class_id, i)
52
def _getPathList(class_id, path, add_index=None, depth=0):
57
path = path.replace("(0)", "").replace("(-1)", "")
59
tmp = path[path.rfind(".") + 1:]
62
plist += [( _normalizeName(tmp), None, None) ]
64
plist += [( _normalizeName(tmp[:elen]), None, None) ]
71
name = prelude.idmef_class_get_child_name(class_id, i)
72
if not name or (name == "file" and class_id == prelude.IDMEF_CLASS_ID_LINKAGE):
75
vtype = prelude.idmef_class_get_child_value_type(class_id, i)
76
space = "\u00a0\u00a0" * depth
78
if vtype == prelude.IDMEF_VALUE_TYPE_CLASS:
79
if add_index and prelude.idmef_class_is_child_list(class_id, i):
84
child_list += [ (space + _normalizeName(name), None, None) ]
85
child_list += _getPathList(prelude.idmef_class_get_child_class(class_id, i), path + "." + name + index, add_index, depth + 1)
87
if vtype == prelude.IDMEF_VALUE_TYPE_ENUM:
88
pval = _getEnumValue(prelude.idmef_class_get_child_class(class_id, i))
92
plist += [( space + name, path + "." + name, pval) ]
96
return plist + child_list
99
def _getClassificationPath(add_empty=False, add_index=None):
102
empty += [("", "none", None)]
105
[("messageid", "alert.messageid", None)] + \
106
_getPathList(prelude.IDMEF_CLASS_ID_CLASSIFICATION, "alert.classification", add_index=add_index) + \
107
_getPathList(prelude.IDMEF_CLASS_ID_ASSESSMENT, "alert.assessment", add_index=add_index) + \
108
_getPathList(prelude.IDMEF_CLASS_ID_OVERFLOW_ALERT, "alert.overflow_alert", add_index=add_index) + \
109
_getPathList(prelude.IDMEF_CLASS_ID_CORRELATION_ALERT, "alert.correlation_alert", add_index=add_index) + \
110
_getPathList(prelude.IDMEF_CLASS_ID_TOOL_ALERT, "alert.tool_alert", add_index=add_index) + \
111
_getPathList(prelude.IDMEF_CLASS_ID_ADDITIONAL_DATA, "alert.additional_data", add_index=add_index)
113
def _getSourcePath(add_empty=False, add_index=None):
116
empty += [("", "none", None)]
118
return empty + _getPathList(prelude.IDMEF_CLASS_ID_SOURCE, "alert.source(0)", add_index=add_index)
120
def _getTargetPath(add_empty=False, add_index=None):
123
empty += [("", "none", None)]
125
return empty + _getPathList(prelude.IDMEF_CLASS_ID_TARGET, "alert.target(0)", add_index=add_index)
127
def _getAnalyzerPath(add_empty=False, add_index=None):
130
empty += [("", "none", None)]
132
return empty + _getPathList(prelude.IDMEF_CLASS_ID_ANALYZER, "alert.analyzer(-1)", add_index=add_index)
135
CLASSIFICATION_FILTERS = _getClassificationPath()
136
CLASSIFICATION_AGGREGATIONS = _getClassificationPath(add_empty=True, add_index="(0)")
138
SOURCE_FILTERS = _getSourcePath()
139
SOURCE_AGGREGATIONS = _getSourcePath(add_empty=True, add_index="(0)")
141
TARGET_FILTERS = _getTargetPath()
142
TARGET_AGGREGATIONS = _getTargetPath(add_empty=True, add_index="(0)")
144
ANALYZER_FILTERS = _getAnalyzerPath()
145
ANALYZER_AGGREGATIONS = _getAnalyzerPath(add_empty=True, add_index="(0)")
149
class AlertListingParameters(MessageListingParameters):
150
allow_extra_parameters = True
154
MessageListingParameters.register(self)
155
self.optional("aggregated_source", list, [ "alert.source(0).node.address(0).address" ], save=True)
156
self.optional("aggregated_target", list, [ "alert.target(0).node.address(0).address" ], save=True)
157
self.optional("aggregated_classification", list, [ "none" ], save=True)
158
self.optional("aggregated_analyzer", list, [ "none" ], save=True)
159
self.optional("filter", str, save=True)
160
self.optional("alert.classification.text", list, [ ], save=True)
161
self.optional("alert.assessment.impact.severity", list, [ "info", "low", "medium", "high", "none" ], save=True)
162
self.optional("alert.assessment.impact.completion", list, [ "succeeded", "failed", "none" ], save=True)
163
self.optional("alert.assessment.impact.type", list, [ "other", "admin", "dos", "file", "recon", "user" ], save=True)
164
self.optional("alert.type", list, ["alert.create_time", "alert.correlation_alert.name", "alert.overflow_alert.program", "alert.tool_alert.name"], save=True)
166
def _checkOperator(self, operator):
167
if operator[0] == "!":
168
operator = operator[1:]
170
if not operator in ("=", "<", ">", "<=", ">=", "~", "~*", "<>", "<>*"):
171
raise view.InvalidParameterValueError("operator", operator)
173
def _loadColumnParam(self, view_name, user, paramlist, column, do_save):
177
for parameter, object in paramlist.items():
178
idx = parameter.find(column + "_object_")
182
num = int(parameter.replace(column + "_object_", "", 1))
183
if num >= self.max_index:
184
self.max_index = num + 1
187
operator = paramlist.get(column + "_operator_" + str(num), "=")
188
self._checkOperator(operator)
191
value = paramlist[column + "_value_" + str(num)]
197
if tmp[1] == object and tmp[2] == operator and tmp[3] == value:
202
sorted.append((num, object, operator, value))
205
self[column] = [ (i[1], i[2], i[3]) for i in sorted ]
209
user.delConfigValueMatch(view_name, "%s_object_%%" % (column))
210
user.delConfigValueMatch(view_name, "%s_operator_%%" % (column))
211
user.delConfigValueMatch(view_name, "%s_value_%%" % (column))
213
for num, obj, operator, value in sorted:
214
user.setConfigValue(view_name, "%s_object_%d" % (column, num), obj)
215
user.setConfigValue(view_name, "%s_operator_%d" % (column, num), operator)
216
user.setConfigValue(view_name, "%s_value_%d" % (column, num), value)
221
def normalize(self, view_name, user):
222
do_save = self.has_key("_save")
223
do_load = MessageListingParameters.normalize(self, view_name, user)
225
for severity in self["alert.assessment.impact.severity"]:
226
if not severity in ("info", "low", "medium", "high", "none"):
227
raise view.InvalidParameterValueError("alert.assessment.impact.severity", severity)
229
for completion in self["alert.assessment.impact.completion"]:
230
if not completion in ("succeeded", "failed", "none"):
231
raise view.InvalidParameterValueError("alert.assessment.impact.completion", completion)
233
for type in self["alert.assessment.impact.type"]:
234
if not type in ("other", "admin", "dos", "file", "recon", "user"):
235
raise view.InvalidParameterValueError("alert.assessment.impact.type", type)
237
for type in self["alert.type"]:
238
if not type in ("alert.create_time", "alert.correlation_alert.name", "alert.overflow_alert.program", "alert.tool_alert.name"):
239
raise view.InvalidParameterValueError("alert.type", type)
242
for column in "classification", "source", "target", "analyzer":
243
ret = self._loadColumnParam(view_name, user, self, column, do_save)
247
if load_saved and do_load and user.configuration.has_key(view_name):
248
for column in "classification", "source", "target", "analyzer":
249
self._loadColumnParam(view_name, user, user.configuration[view_name], column, do_save)
251
for category in "classification", "source", "target", "analyzer":
253
for path in self["aggregated_%s" % category]:
255
if self["aggregated_%s" % category].count(path) > 1:
256
self["aggregated_%s" % category].remove(path)
259
self["aggregated_%s" % category][i] = path[1:]
265
class SensorAlertListingParameters(AlertListingParameters):
267
AlertListingParameters.register(self)
268
self.mandatory("analyzerid", long)
270
def normalize(self, view_name, user):
271
AlertListingParameters.normalize(self, view_name, user)
272
self["analyzer"].insert(0, ("alert.analyzer.analyzerid", "=", str(self["analyzerid"])))
275
class CorrelationAlertListingParameters(AlertListingParameters):
277
AlertListingParameters.register(self)
278
self.optional("aggregated_source", list, [ ], save=True)
279
self.optional("aggregated_target", list, [ ], save=True)
280
self.optional("alert.type", list, ["alert.correlation_alert.name"], save=True)
282
def normalize(self, view_name, user):
283
AlertListingParameters.normalize(self, view_name, user)
286
class ToolAlertListingParameters(AlertListingParameters):
288
AlertListingParameters.register(self)
289
self.optional("aggregated_source", list, [ ], save=True)
290
self.optional("aggregated_target", list, [ ], save=True)
291
self.optional("alert.type", list, ["alert.tool_alert.name"], save=True)
293
def normalize(self, view_name, user):
294
AlertListingParameters.normalize(self, view_name, user)
297
class ListedAlert(ListedMessage):
298
def __init__(self, *args, **kwargs):
299
apply(ListedMessage.__init__, (self, ) + args, kwargs)
302
def _getKnownValue(self, direction, key):
303
return { "alert.%s.service.port" % direction: ("service", None),
304
"alert.%s.node.address.address" % direction: ("addresses", self._setMessageDirectionAddress),
305
"alert.%s.node.name" % direction: ("addresses", self._setMessageDirectionNodeName),
308
def _initValue(self, dataset, name, value):
309
dataset[name] = value
311
def _initDirection(self, dataset):
312
self._initValue(dataset, "protocol", { "value": None })
313
self._initValue(dataset, "service", { "value": None })
314
self._initValue(dataset, "addresses", [ ])
315
self._initValue(dataset, "listed_values", [ ])
316
self._initValue(dataset, "aggregated_hidden", 0)
319
def _initDirectionIfNeeded(self, direction):
320
if len(self[direction]) == 0:
321
self[direction].append(self._initDirection({ }))
323
def _setMainAndExtraValues(self, dataset, name, object_main, object_extra):
324
if object_main != None:
325
dataset[name] = { "value": object_main }
326
dataset[name + "_extra"] = { "value": object_extra }
329
dataset[name] = { "value": object_extra }
330
dataset[name + "_extra"] = { "value": None }
332
def _guessAddressCategory(self, address):
333
if re.compile("\d\.\d\.\d\.\d").match(address):
336
elif re.compile(".*@.*").match(address):
339
elif address.count(":") > 1:
344
def _setMessageDirectionAddress(self, dataset, direction, address, category=None):
346
category = self._guessAddressCategory(address)
348
hfield = self.createHostField("alert.%s.node.address.address" % direction, address, category=category, direction=direction)
349
dataset["addresses"].append(hfield)
351
def _setMessageDirectionNodeName(self, dataset, direction, name):
352
dataset["addresses"].append(self.createHostField("alert.%s.node.name" % direction, name, direction=direction))
354
def _setMessageDirectionOther(self, dataset, direction, path, value, extra_path=None, extra=None):
361
extra = extra_path = None
364
l[-2] = l[-2].replace("(0)", "")
366
if l[-2] != direction:
367
name = _normalizeName(l[-2]) + " "
373
item = (name, self.createInlineFilteredField(path, value, direction), extra)
374
if not item in dataset["listed_values"]:
375
dataset["listed_values"].append(item)
378
def _setMessageDirection(self, dataset, direction, obj):
379
dataset["interface"] = { "value": obj["interface"] }
381
for userid in obj["user.user_id"]:
382
self._setMessageDirectionOther(dataset, direction, "alert.%s.user.user_id.name" % direction, userid["name"],
383
"alert.%s.user.user_id.number" % direction, userid["number"])
385
name = obj["node.name"]
387
self._setMessageDirectionNodeName(dataset, direction, name)
389
for addr in obj["node.address"]:
390
self._setMessageDirectionAddress(dataset, direction, addr["address"], addr["category"])
392
self._setMessageDirectionOther(dataset, direction, "alert.%s.process.name" % direction, obj["process.name"],
393
"alert.%s.process.pid" % direction, extra=obj["process.pid"])
396
if obj["service.iana_protocol_name"]:
397
proto = obj["service.iana_protocol_name"]
399
elif obj["service.iana_protocol_number"]:
400
num = obj["service.iana_protocol_number"]
401
proto = utils.protocol_number_to_name(num)
404
proto = obj["service.protocol"]
406
self._setMainAndExtraValues(dataset, "protocol", proto, None)
407
self._setMainAndExtraValues(dataset, "service", obj["service.port"], None)
409
dataset["files"] = []
411
def _lookupDataset(self, dlist, dataset):
413
if dset.items() == dataset.items():
418
def _setMessageSource(self, message):
421
for source in message["alert.source"]:
423
self._initDirection(dataset)
424
self._setMessageDirection(dataset, "source", source)
426
if not self._lookupDataset(self["source"], dataset):
428
if self._source_index == self.env.max_aggregated_source:
432
self._source_index += 1
433
self["source"].append(dataset)
435
self["aggregated_source_total"] += total
436
self["aggregated_source_hidden"] += (total - index)
438
def _setMessageTarget(self, message):
442
for target in message["alert.target"]:
444
self._initDirection(dataset)
445
self._setMessageDirection(dataset, "target", target)
448
for f in target["file"]:
449
if f["path"] in flist:
452
flist.append(f["path"])
453
self._setMessageDirectionOther(dataset, "target", "alert.target.file.path", f["path"])
455
if not self._lookupDataset(self["target"], dataset):
457
if self._target_index == self.env.max_aggregated_target:
461
self._target_index += 1
462
self["target"].append(dataset)
464
self["aggregated_target_total"] += total
465
self["aggregated_target_hidden"] += (total - index)
467
def _setMessageClassificationReferences(self, dataset, message):
468
dataset["classification_references"] = [ ]
469
for ref in message["alert.classification.reference"]:
472
origin = ref["origin"]
480
urlstr = "https://www.prelude-ids.com/reference_details.php?origin=%s&name=%s" % (ref["origin"], ref["name"])
481
if ref["origin"] in ("vendor-specific", "user-specific"):
482
urlstr += "&url=" + ref["url"]
484
dataset["classification_references"].append((urlstr, fstr))
486
def _setMessageClassification(self, dataset, message):
487
self._setMessageClassificationReferences(dataset, message)
488
dataset["classification"] = self.createInlineFilteredField("alert.classification.text",
489
message["alert.classification.text"])
491
def _fetchInfoFromLinkedMessage(self, criteria, source, target):
492
result = self.env.idmef_db.getAlertIdents(criteria)
494
idmef = self.env.idmef_db.getAlert(ident)
497
self._setMessageSource(idmef)
500
self._setMessageTarget(idmef)
503
def _setMessageAlertIdentInfo(self, message, alert, ident):
504
fetch_classification_info = fetch_source_info = fetch_target_info = True
509
source_analyzer = None
511
for alertident in alert["alertident"]:
514
# IDMEF draft 14 page 27
515
# If the "analyzerid" is not provided, the alert is assumed to have come
516
# from the same analyzer that is sending the Alert.
518
analyzerid = alertident["analyzerid"]
521
analyzerid = source_analyzer
523
for a in message["analyzer"]:
525
source_analyzer = analyzerid = a["analyzerid"]
528
params["analyzer_object_%d" % i] = "alert.analyzer.analyzerid"
529
params["analyzer_value_%d" % i] = analyzerid
530
params["classification_object_%d" % i] = "alert.messageid"
531
params["classification_value_%d" % i] = alertident["alertident"]
533
criteria.append("(alert.messageid = '%s' && alert.analyzer.analyzerid = '%s')" % (utils.escape_criteria(alertident["alertident"]), utils.escape_criteria(analyzerid)))
535
source = message["alert.source"]
536
target = message["alert.target"]
537
if not source or not target:
538
self._fetchInfoFromLinkedMessage(" || ".join(criteria), source, target)
540
self["sub_alert_number"] = i
541
self["sub_alert_name"] = alert["name"]
542
self["sub_alert_link"] = self.createMessageLink(ident, "alert_summary")
544
tmp = self.parameters
545
tmp -= [ "timeline_unit", "timeline_value", "offset",
546
"aggregated_classification", "aggregated_source",
547
"aggregated_target", "aggregated_analyzer", "alert.type", "alert.assessment.impact.severity",
548
"alert.assessment.impact.completion" ]
550
tmp["aggregated_target"] = tmp["aggregated_source"] = \
551
tmp["aggregated_classification"] = tmp["aggregated_analyzer"] = "none"
553
params["timeline_unit"] = "unlimited"
554
self["sub_alert_display"] = utils.create_link("alert_listing", tmp + params)
556
def _setClassificationInfos(self, dataset, message, ident):
558
dataset["display"] = self.createMessageLink(ident, "alert_summary")
559
dataset["severity"] = { "value": message["alert.assessment.impact.severity"] }
560
dataset["completion"] = { "value": message["alert.assessment.impact.completion"] }
562
def _setMessageTime(self, message):
563
self["time"] = self.createTimeField(message["alert.create_time"], self.timezone)
564
if (message["alert.analyzer_time"] != None and
565
abs(int(message["alert.create_time"]) - int(message["alert.analyzer_time"])) > 60):
566
self["analyzer_time"] = self.createTimeField(message["alert.analyzer_time"], self.timezone)
568
self["analyzer_time"] = { "value": None }
570
def addSensor(self, name, model, node_name):
572
self["sensors"].append(sensor)
575
sensor["name"] = self.createInlineFilteredField("alert.analyzer.name", name, direction="analyzer")
578
sensor["name"] = self.createInlineFilteredField("alert.analyzer.model", model, direction="analyzer")
580
sensor["node_name"] = self.createInlineFilteredField("alert.analyzer.node.name", node_name, direction="analyzer")
582
def setMessage(self, message, ident):
583
self["infos"] = [ { } ]
585
self.addSensor(message["alert.analyzer(-1).name"], message["alert.analyzer(-1).model"], message["alert.analyzer(-1).node.name"])
586
self._setMessageTime(message)
588
dataset = self["infos"][0]
589
self._setClassificationInfos(dataset, message, ident)
590
self._setMessageClassification(dataset, message)
592
if message["alert.correlation_alert"]:
593
self["sub_alert_type"] = "Correlation Alert"
594
self._setMessageAlertIdentInfo(message, message["alert.correlation_alert"], ident)
596
elif message["alert.tool_alert"]:
597
self["sub_alert_type"] = "Tool Alert"
598
self._setMessageAlertIdentInfo(message, message["alert.tool_alert"], ident)
600
if not self["source"]:
601
self._setMessageSource(message)
603
if not self["target"]:
604
self._setMessageTarget(message)
606
def setMessageDirectionGeneric(self, direction, object, value):
607
self._initDirectionIfNeeded(direction)
608
dataset = self[direction][-1]
611
dset_name, function = self._getKnownValue(direction, object.replace("(0)", ""))
613
return self._setMessageDirectionOther(dataset, direction, object, value)
616
function(dataset, direction, value)
618
if type(dataset[dset_name]) is list:
619
dataset[dset_name].append({ "value": value })
621
dataset[dset_name]["value"] = value
624
self["sensors"] = [ ]
627
self["sub_alert_name"] = None
628
self["aggregated_source_total"] = 0
629
self["aggregated_source_hidden"] = 0
630
self["aggregated_source_expand"] = 0
631
self["aggregated_target_total"] = 0
632
self["aggregated_target_hidden"] = 0
633
self["aggregated_target_expand"] = 0
634
self._source_index = 0
635
self._target_index = 0
638
class ListedAggregatedAlert(ListedAlert):
639
def __init__(self, *args, **kwargs):
640
apply(ListedAlert.__init__, (self,) + args, kwargs)
641
self["aggregated"] = True
642
self["aggregated_classification_hidden"] = 0
647
def setTime(self, time_min, time_max):
648
self["time_min"] = self.createTimeField(time_min, self.parameters["timezone"])
649
self["time_max"] = self.createTimeField(time_max, self.parameters["timezone"])
651
def setCriteriaForDeletion(self, delete_criteria):
652
self["delete"] = urllib.quote_plus(" && ".join(delete_criteria))
654
def setInfos(self, count, classification, severity, completion):
656
"classification_references": "",
658
"classification": self.createInlineFilteredField("alert.classification.text", classification),
659
"severity": { "value": severity },
660
"completion": { "value": completion }
663
self["infos"].append(infos)
669
class AlertListing(MessageListing, view.View):
670
view_name = "alert_listing"
671
view_parameters = AlertListingParameters
672
view_permissions = [ User.PERM_IDMEF_VIEW ]
673
view_template = "AlertListing"
676
summary_view = "alert_summary"
677
details_view = "alert_details"
678
listed_alert = ListedAlert
679
listed_aggregated_alert = ListedAggregatedAlert
680
alert_type_default = ["alert.create_time", "alert.correlation_alert.name", "alert.overflow_alert.program", "alert.tool_alert.name"]
683
self._max_aggregated_classifications = int(env.config.general.getOptionValue("max_aggregated_classifications", 10))
685
def _getMessageIdents(self, criteria, limit=-1, offset=-1):
686
return self.env.idmef_db.getAlertIdents(criteria, limit, offset)
688
def _fetchMessage(self, ident):
689
return self.env.idmef_db.getAlert(ident)
691
def _setMessage(self, message, ident):
692
msg = self.listed_alert(self.view_name, self.env, self.parameters)
693
msg.setMessage(message, ident)
694
msg["aggregated"] = False
695
msg["delete"] = ident
699
def _getFilters(self, storage, login):
700
return storage.getAlertFilters(login)
702
def _getFilter(self, storage, login, name):
703
return storage.getAlertFilter(login, name)
705
def _deleteMessage(self, ident):
706
self.env.idmef_db.deleteAlert(ident)
708
def _lists_have_same_content(self, l1, l2):
716
def _applyOptionalEnumFilter(self, criteria, column, object, values):
717
if ( self.parameters.has_key(object) and not self._lists_have_same_content(self.parameters[object], values) ):
720
if value in self.parameters[object]:
722
new.append("!%s" % (object))
724
new.append("%s = '%s'" % (object, utils.escape_criteria(value)))
726
criteria.append("(" + " || ".join(new) + ")")
727
self.dataset[object] = self.parameters[object]
728
self.dataset[column + "_filtered"] = True
730
self.dataset[object] = values
732
def _applyAlertTypeFilters(self, criteria):
733
if "alert.create_time" in self.parameters["alert.type"]:
740
for param in ["alert.correlation_alert.name", "alert.overflow_alert.program", "alert.tool_alert.name"]:
742
if param in self.parameters["alert.type"]:
743
new.append("%s" % param)
745
if not param in self.parameters["alert.type"]:
746
new.append("!%s" % param)
748
self.dataset["alert.type"] = self.parameters["alert.type"]
754
criteria.append("(" + " || ".join(new) + ")")
756
criteria.append("(" + " && ".join(new) + ")")
758
if not self._lists_have_same_content(self.parameters["alert.type"], self.alert_type_default):
759
self.dataset["classification_filtered"] = True
761
def _applyClassificationFilters(self, criteria):
762
self._applyAlertTypeFilters(criteria)
764
self._applyOptionalEnumFilter(criteria, "classification", "alert.assessment.impact.severity",
765
["info", "low", "medium", "high", "none"])
766
self._applyOptionalEnumFilter(criteria, "classification", "alert.assessment.impact.completion",
767
["failed", "succeeded", "none"])
768
self._applyOptionalEnumFilter(criteria, "classification", "alert.assessment.impact.type",
769
["other", "admin", "dos", "file", "recon", "user"])
772
def _applyCheckboxFilters(self, criteria, type):
773
def get_string((object, operator, value)):
774
return "%s %s '%s'" % (object, operator, utils.escape_criteria(value))
776
if self.parameters[type]:
778
# If one object is specified more than one time, and since this object
779
# can not have two different value, we want to apply an OR operator.
781
# We apply an AND operator between the different objects.
784
for obj in self.parameters[type]:
785
if merge.has_key(obj[0]):
786
merge[obj[0]] += [ obj ]
788
merge[obj[0]] = [ obj ]
791
for key in iter(merge):
795
newcrit += "(" + " || ".join(map(get_string, merge[key])) + ")"
798
criteria.append(newcrit)
800
self.dataset[type] = [ (path.replace("(0)", "").replace("(-1)", ""), operator, value) for path, operator, value in self.parameters[type] ]
801
self.dataset["%s_filtered" % type] = True
803
self.dataset[type] = [ ("", "", "") ]
804
self.dataset["%s_filtered" % type] = False
806
def _applyFilters(self, criteria):
807
self._applyCheckboxFilters(criteria, "classification")
808
self._applyClassificationFilters(criteria)
810
self._applyCheckboxFilters(criteria, "source")
811
self._applyCheckboxFilters(criteria, "target")
812
self._applyCheckboxFilters(criteria, "analyzer")
815
def _getMissingAggregatedInfos(self, message, path_value_hash, parameters, criteria2, aggregated_count):
820
for path in ("alert.classification.text", "alert.analyzer(-1).node.name",
821
"alert.analyzer(-1).name", "alert.analyzer(-1).model", "alert.assessment.impact.severity",
822
"alert.assessment.impact.completion"):
824
if not path_value_hash.has_key(path):
825
selection_list += [ (path, index) ]
828
selection = [ "%s/group_by" % i[0] for i in selection_list ]
830
alert_list = self.env.idmef_db.getValues( selection + ["max(alert.messageid)", "count(alert.messageid)" ], criteria2)
834
for values in alert_list:
835
for path, index in selection_list:
836
path_value_hash[path] = values[index]
838
max_messageid = values[-2]
839
alert_count = values[-1]
841
classification = path_value_hash["alert.classification.text"]
842
analyzer_name = path_value_hash["alert.analyzer(-1).name"]
843
analyzer_model = path_value_hash["alert.analyzer(-1).model"]
844
analyzer_node_name = path_value_hash["alert.analyzer(-1).node.name"]
845
severity = path_value_hash["alert.assessment.impact.severity"]
846
completion = path_value_hash["alert.assessment.impact.completion"]
848
alertkey = (classification or "") + "-" + (severity or "") + "-" + (completion or "")
850
if alertsraw.has_key(alertkey):
851
alertsraw[alertkey][-2] += alert_count
853
alertsraw[alertkey] = ( [classification, severity, completion, alert_count, max_messageid] )
855
nodekey = (analyzer_name or analyzer_model or "") + "-" + (analyzer_node_name or "")
856
if not nodesraw.has_key(nodekey):
857
message.addSensor(analyzer_name, analyzer_model, analyzer_node_name)
858
nodesraw[nodekey] = True
860
res = alertsraw.values()
861
res.sort(lambda x, y: cmp_severities(x[1], y[1]))
865
for classification, severity, completion, count, messageid in res:
866
if result_count >= self._max_aggregated_classifications:
871
message["aggregated_classifications_hidden"] -= count
872
infos = message.setInfos(count, classification, severity, completion)
875
ident = self.env.idmef_db.getAlertIdents("alert.messageid = '%s'" % utils.escape_criteria(messageid))[0]
876
if aggregated_count == 1:
878
message.setMessage(self._fetchMessage(ident), ident)
880
infos["display"] = message.createMessageLink(ident, "alert_summary")
885
entry_param["classification_object_%d" % self.parameters.max_index] = "alert.classification.text"
886
entry_param["classification_operator_%d" % self.parameters.max_index] = "="
887
entry_param["classification_value_%d" % self.parameters.max_index] = utils.escape_criteria(classification)
889
entry_param["alert.assessment.impact.severity"] = severity or "none"
890
entry_param["alert.assessment.impact.completion"] = completion or "none"
892
entry_param["aggregated_target"] = \
893
entry_param["aggregated_source"] = \
894
entry_param["aggregated_analyzer"] = \
895
entry_param["aggregated_classification"] = "none"
897
infos["display"] = utils.create_link(self.view_name, self.parameters -
898
[ "offset", "aggregated_classification",
899
"aggregated_source", "aggregated_target", "aggregated_analyzer" ] +
900
parameters + entry_param)
903
def _getPathValueType(self, path):
904
p = prelude.idmef_path_new(path)
905
t = prelude.idmef_path_get_value_type(p, -1)
906
prelude.idmef_path_destroy(p)
909
def _setAggregatedMessagesNoValues(self, criteria, ag_s, ag_t, ag_c, ag_a):
910
ag_list = ag_s + ag_t + ag_c + ag_a
913
selection = [ "%s/group_by" % path for path in ag_list ] + \
914
[ "count(alert.create_time)", "max(alert.create_time)/order_desc" ]
916
results = self.env.idmef_db.getValues(selection, criteria)
917
total_results = len(results)
919
for values in results[self.parameters["offset"]:self.parameters["offset"]+self.parameters["limit"]]:
921
aggregated_source_values = []
922
aggregated_target_values = []
923
aggregated_classification_values = []
924
aggregated_analyzer_values = []
928
aggregated_source_values = values[:len(ag_s)]
931
last = start + len(ag_t)
932
aggregated_target_values = values[start:last]
936
last = start + len(ag_c)
937
if values[start:last]:
938
aggregated_classification_values = values[start:last]
942
last = start + len(ag_a)
943
if values[start:last]:
944
aggregated_analyzer_values = values[start:last]
948
aggregated_count = values[start]
949
time_min = values[start + 1]
951
criteria2 = criteria[:]
952
delete_criteria = [ ]
953
message = self.listed_aggregated_alert(self.view_name, self.env, self.parameters)
956
for path, value in zip(ag_list, values[:start]):
957
valueshash[path] = value
959
if path.find("source") != -1:
961
elif path.find("target") != -1:
967
if self._getPathValueType(path) != prelude.IDMEF_VALUE_TYPE_STRING:
968
criterion = "! %s" % (path)
970
criterion = "(! %s || %s == '')" % (path, path)
972
criterion = "%s == '%s'" % (path, utils.escape_criteria(str(value)))
973
if direction != None:
974
message.setMessageDirectionGeneric(direction, path, value)
976
criteria2.append(criterion)
977
delete_criteria.append(criterion)
979
time_min = self.env.idmef_db.getValues(["alert.create_time/order_asc"], criteria2, limit=1)[0][0]
980
time_max = self.env.idmef_db.getValues(["alert.create_time/order_desc"], criteria2, limit=1)[0][0]
982
parameters = self._createAggregationParameters(aggregated_classification_values,
983
aggregated_source_values, aggregated_target_values, aggregated_analyzer_values)
985
message["aggregated_classifications_total"] = aggregated_count
986
message["aggregated_classifications_hidden"] = aggregated_count
987
message["aggregated_classifications_hidden_expand"] = utils.create_link(self.view_name,
992
"aggregated_analyzer" ]
994
{ "aggregated_classification":
995
"alert.classification.text" } )
997
self._getMissingAggregatedInfos(message, valueshash, parameters, criteria2, aggregated_count)
999
delete_criteria.append("alert.create_time >= '%s'" % time_min.toYMDHMS())
1000
delete_criteria.append("alert.create_time <= '%s'" % time_max.toYMDHMS())
1002
self.dataset["messages"].append(message)
1003
message.setTime(time_min, time_max)
1004
message.setCriteriaForDeletion(delete_criteria)
1006
return total_results
1009
def _createAggregationParameters(self, aggregated_classification_values, aggregated_source_values, aggregated_target_values, aggregated_analyzer_values):
1012
for values, column in ((aggregated_classification_values, "classification"),
1013
(aggregated_source_values, "source"),
1014
(aggregated_target_values, "target"),
1015
(aggregated_analyzer_values, "analyzer")):
1016
i = self.parameters.max_index
1017
for path, value in zip(self.parameters["aggregated_%s" % column], values):
1021
parameters["%s_object_%d" % (column, i)] = path.replace("(0)", "").replace("(-1)", "")
1022
parameters["%s_operator_%d" % (column, i)] = "="
1023
parameters["%s_value_%d" % (column, i)] = value
1029
def _setMessages(self, criteria):
1030
self.dataset["messages"] = [ ]
1031
self.dataset["aggregated_source"] = self.parameters["aggregated_source"]
1032
self.dataset["aggregated_target"] = self.parameters["aggregated_target"]
1033
self.dataset["aggregated_classification"] = self.parameters["aggregated_classification"]
1034
self.dataset["aggregated_analyzer"] = self.parameters["aggregated_analyzer"]
1036
ag_s = self.parameters["aggregated_source"][:]
1037
ag_t = self.parameters["aggregated_target"][:]
1038
ag_c = self.parameters["aggregated_classification"][:]
1039
ag_a = self.parameters["aggregated_analyzer"][:]
1041
for l in ag_s, ag_t, ag_c, ag_a:
1045
if len(ag_s + ag_t + ag_c + ag_a) > 0:
1046
return self._setAggregatedMessagesNoValues(criteria, ag_s, ag_t, ag_c, ag_a)
1048
results = self.env.idmef_db.getAlertIdents(criteria)
1050
for ident in results[self.parameters["offset"] : self.parameters["offset"] + self.parameters["limit"]]:
1051
message = self.env.idmef_db.getAlert(ident)
1052
dataset = self._setMessage(message, ident)
1053
self.dataset["messages"].append(dataset)
1057
def _setDatasetConstants(self):
1058
self.dataset["classification_filters"] = CLASSIFICATION_FILTERS
1059
self.dataset["classification_aggregations"] = CLASSIFICATION_AGGREGATIONS
1060
self.dataset["source_filters"] = SOURCE_FILTERS
1061
self.dataset["source_aggregations"] = SOURCE_AGGREGATIONS
1062
self.dataset["target_filters"] = TARGET_FILTERS
1063
self.dataset["target_aggregations"] = TARGET_AGGREGATIONS
1064
self.dataset["analyzer_filters"] = ANALYZER_FILTERS
1065
self.dataset["analyzer_aggregations"] = ANALYZER_AGGREGATIONS
1068
self._deleteMessages()
1069
self._setDatasetConstants()
1071
self.dataset["filters"] = self.env.db.getAlertFilterNames(self.user.login)
1072
self.dataset["current_filter"] = self.parameters.get("filter", "")
1076
if self.parameters.has_key("filter"):
1077
filter = self.env.db.getAlertFilter(self.user.login, self.parameters["filter"])
1079
criteria.append("(%s)" % str(filter))
1082
if self.parameters.has_key("timeline_unit") and self.parameters["timeline_unit"] != "unlimited":
1083
start, end = self._getTimelineRange()
1084
criteria.append("alert.create_time >= '%s' && alert.create_time < '%s'" % (str(start), str(end)))
1086
self._applyFilters(criteria)
1087
self._adjustCriteria(criteria)
1089
self._setTimeline(start, end)
1090
self._setNavPrev(self.parameters["offset"])
1092
self._setHiddenParameters()
1094
self.dataset["messages"] = [ ]
1095
total = self._setMessages(criteria)
1097
self.dataset["nav.from"] = self.parameters["offset"] + 1
1098
self.dataset["nav.to"] = self.parameters["offset"] + len(self.dataset["messages"])
1099
self.dataset["limit"] = self.parameters["limit"]
1100
self.dataset["total"] = total
1101
self.dataset["correlation_alert_view"] = False
1103
self._setNavNext(self.parameters["offset"], total)
1107
class ListedSensorAlert(ListedAlert):
1108
view_name = "sensor_alert_listing"
1112
class ListedSensorAggregatedAlert(ListedAggregatedAlert):
1113
view_name = "sensor_alert_listing"
1116
class CorrelationAlertListing(AlertListing, view.View):
1117
view_name = "correlation_alert_listing"
1118
view_parameters = CorrelationAlertListingParameters
1119
alert_type_default = [ "alert.correlation_alert.name" ]
1122
class ToolAlertListing(AlertListing, view.View):
1123
view_name = "tool_alert_listing"
1124
view_parameters = ToolAlertListingParameters
1125
alert_type_default = [ "alert.tool_alert.name" ]
1129
class SensorAlertListing(AlertListing, view.View):
1130
view_name = "sensor_alert_listing"
1131
view_parameters = SensorAlertListingParameters
1132
view_permissions = [ User.PERM_IDMEF_VIEW ]
1133
view_template = "SensorAlertListing"
1135
listed_alert = ListedSensorAlert
1136
listed_aggregated_alert = ListedSensorAggregatedAlert
1138
def _setHiddenParameters(self):
1139
AlertListing._setHiddenParameters(self)
1140
self.dataset["hidden_parameters"].append(("analyzerid", self.parameters["analyzerid"]))
1143
AlertListing.render(self)
1144
self.dataset["analyzer_infos"] = self.env.idmef_db.getAnalyzer(self.parameters["analyzerid"])