-
Notifications
You must be signed in to change notification settings - Fork 29
Expand file tree
/
Copy pathRule.py
More file actions
175 lines (138 loc) · 5.98 KB
/
Rule.py
File metadata and controls
175 lines (138 loc) · 5.98 KB
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
import re
import logging
import redis
from ransack import Parser, Filter
from idea import lite
logger = logging.getLogger(__name__)
STAT_KEYPREFIX = "nemea"
redis_con = redis.StrictRedis()
def UpdateCounter(prefix, module, ruleid, result, actionid, actiontype):
"""Increment counter in redis, key is composed from:
prefix - string identifying all redis keys (NEMEA)
module - name of reporter
ruleid - identifier of rule from config
result - True / False
actionid - identifier of custom action from config
actiontype - type of the action
"""
try:
if redis_con:
key = f"{prefix}|{module}|{ruleid}|{result}|{actionid}|{actiontype}"
redis_con.incr(key)
except redis.exceptions.ConnectionError:
logging.error("redis: Could not update statistics.")
def clearCounters(prefix, module):
try:
if redis_con:
key = f"{prefix}|{module}|*"
for i in redis_con.keys(key):
redis_con.delete(i)
except redis.exceptions.ConnectionError:
logging.error("redis: Could not update statistics.")
class Rule():
def __init__(self, rule, actions, parser=None, module_name=""):
if not "condition" in rule:
raise SyntaxError("Missing 'condition' in the rule: " + str(rule))
if not "id" in rule:
raise SyntaxError("Missing 'id' in the rule: " + str(rule))
# Check if we got parser instance
if parser is None:
self.parser = Parser()
else:
self.parser = parser
# Instantiate filter
self.__filter = Filter()
# Store rule condition in raw form
self.__conditionRaw = rule["condition"]
self.module_name = module_name
# Set inner rule ID
self.id = rule["id"]
# Store the parsed condition
self.__condition = self.__conditionRaw
if (self.__condition != None):
self.parseRule()
self.__actions = list()
self.__elseactions = list()
# Associate actions
if "actions" in rule:
for actionId in rule["actions"]:
try:
logger.debug("Rule %s inserting %s", self.id, actionId)
self.__actions.append(actions[actionId])
except KeyError as e:
raise SyntaxError("Missing action with ID %s" % str(e))
# Associate elseactions
if "elseactions" in rule:
for actionId in rule["elseactions"]:
try:
self.__elseactions.append(actions[actionId])
except KeyError as e:
raise SyntaxError("Missing elseaction with ID %s" % str(e))
def parseRule(self):
cond = str(self.__condition).lower()
if cond in ["none", "null", "true"]:
self.__condition = True
elif cond == "false":
self.__condition = False
else:
try:
self.__condition = self.parser.parse(self.__condition)
except Exception as e:
raise SyntaxError("Error while parsing condition: {0}\nOriginal exception: {1}".format(self.__condition, e))
def filter(self, record):
"""
Filter given record based on rule's condition
@note If the rule's condition is empty, record is always matched.
@return Boolean
"""
# The message must be converted via idea module
if not isinstance(record, lite.Idea):
logger.info("Converting message to IDEA")
record = lite.Idea(record)
logger.debug(record)
logger.debug(self.__condition)
if self.__condition == None or self.__condition == True:
# Rule condition is empty (tautology) - should always match the record
res = True
elif self.__condition == False:
res = False
else:
# Match the record with non-empty rule's condition
res = self.__filter.eval(self.__condition, record)
logger.debug("RESULT: %s", res)
return res
def actions(self, record):
for action in self.__actions:
UpdateCounter(STAT_KEYPREFIX, self.module_name, self.id, True, action.actionId, action.actionType)
action.run(record)
def elseactions(self, record):
for action in self.__elseactions:
UpdateCounter(STAT_KEYPREFIX, self.module_name, self.id, False, action.actionId, action.actionType)
action.run(record)
def __repr__(self):
return self.__conditionRaw
def __str__(self):
actions = []
for i in self.__actions:
key = "{prefix}|{module}|{ruleid}|{result}|{actionid}|{actiontype}".format(prefix=STAT_KEYPREFIX,
module=self.module_name, ruleid=self.id,
result="True", actionid=i.actionId,
actiontype=i.actionType)
try:
cnt = redis_con.get(key)
if cnt:
cnt = cnt.decode("utf-8")
else:
cnt = 0
except redis.exceptions.ConnectionError:
logging.error("redis: Could not retrieve statistics.")
cnt = 0
actions.append(f"{i.actionId} ({i.actionType}) [{cnt}x]")
elseactions = []
for i in self.__elseactions:
elseactions.append("{0} ({1})".format(i.actionId, i.actionType))
return "{0}: {1}\n{2}{3}".format(self.id, " ".join([i.strip() for i in str(self.__conditionRaw).split("\n")]),
"\tActions: " + (", ".join(actions)) + "\n" if actions else "",
"\tElse Actions: " + (", ".join(elseactions)) + "\n" if elseactions else "")
def rule(self):
return str(self.__condition)