9
9
from pathlib import Path
10
10
from uuid import uuid4
11
11
12
+ import pymisp
13
+
12
14
from intelmq .lib .bot import OutputBot
13
15
from intelmq .lib .exceptions import MissingDependencyError
16
+ from ....lib .message import Message , MessageFactory
14
17
from intelmq .lib .mixins import CacheMixin
15
18
from intelmq .lib .utils import parse_relative
16
19
@@ -30,8 +33,11 @@ class MISPFeedOutputBot(OutputBot, CacheMixin):
30
33
bulk_save_count : int = None
31
34
misp_org_name = None
32
35
misp_org_uuid = None
33
- output_dir : str = "/opt/intelmq/var/lib/bots/mispfeed-output" # TODO: should be path
36
+ output_dir : str = (
37
+ "/opt/intelmq/var/lib/bots/mispfeed-output" # TODO: should be path
38
+ )
34
39
_is_multithreadable : bool = False
40
+ attribute_mapping : dict = None
35
41
36
42
@staticmethod
37
43
def check_output_dir (dirname ):
@@ -56,11 +62,13 @@ def init(self):
56
62
if self .interval_event is None :
57
63
self .timedelta = datetime .timedelta (hours = 1 )
58
64
else :
59
- self .timedelta = datetime .timedelta (minutes = parse_relative (self .interval_event ))
65
+ self .timedelta = datetime .timedelta (
66
+ minutes = parse_relative (self .interval_event )
67
+ )
60
68
61
- if (self .output_dir / ' .current' ).exists ():
69
+ if (self .output_dir / " .current" ).exists ():
62
70
try :
63
- with (self .output_dir / ' .current' ).open () as f :
71
+ with (self .output_dir / " .current" ).open () as f :
64
72
self .current_file = Path (f .read ())
65
73
66
74
if self .current_file .exists ():
@@ -127,12 +135,49 @@ def process(self):
127
135
128
136
def _add_message_to_feed (self , message : dict ):
129
137
obj = self .current_event .add_object (name = "intelmq_event" )
138
+ if not self .attribute_mapping :
139
+ self ._default_mapping (obj , message )
140
+ else :
141
+ self ._custom_mapping (obj , message )
142
+
143
+ def _default_mapping (self , obj : pymisp .MISPObject , message : dict ):
130
144
for object_relation , value in message .items ():
131
145
try :
132
146
obj .add_attribute (object_relation , value = value )
133
147
except NewAttributeError :
134
148
# This entry isn't listed in the harmonization file, ignoring.
135
- pass
149
+ self .logger .warning (
150
+ "Object relation %s not exists in MISP definition, ignoring" ,
151
+ object_relation ,
152
+ )
153
+
154
+ def _extract_misp_attribute_kwargs (self , message : dict , definition : dict ) -> dict :
155
+ # For caching and default mapping, the serialized version is the right format to work on.
156
+ # However, for any custom mapping the Message object is more sufficient as it handles
157
+ # subfields.
158
+ message = MessageFactory .from_dict (
159
+ message , harmonization = self .harmonization , default_type = "Event"
160
+ )
161
+ result = {}
162
+ for parameter , value in definition .items ():
163
+ # Check if the value is a harmonization key or a static value
164
+ if isinstance (value , str ) and (
165
+ value in self .harmonization ["event" ]
166
+ or value .split ("." , 1 )[0 ] in self .harmonization ["event" ]
167
+ ):
168
+ result [parameter ] = message .get (value )
169
+ else :
170
+ result [parameter ] = value
171
+ return result
172
+
173
+ def _custom_mapping (self , obj : pymisp .MISPObject , message : dict ):
174
+ for object_relation , definition in self .attribute_mapping .items ():
175
+ obj .add_attribute (
176
+ object_relation ,
177
+ value = message [object_relation ],
178
+ ** self ._extract_misp_attribute_kwargs (message , definition ),
179
+ )
180
+ # In case of manual mapping, we want to fail if it produces incorrect values
136
181
137
182
def _generate_feed (self , message : dict = None ):
138
183
if message :
@@ -151,18 +196,27 @@ def _generate_feed(self, message: dict = None):
151
196
152
197
@staticmethod
153
198
def check (parameters ):
154
- if ' output_dir' not in parameters :
199
+ if " output_dir" not in parameters :
155
200
return [["error" , "Parameter 'output_dir' not given." ]]
156
201
try :
157
- created = MISPFeedOutputBot .check_output_dir (parameters [' output_dir' ])
202
+ created = MISPFeedOutputBot .check_output_dir (parameters [" output_dir" ])
158
203
except OSError :
159
- return [["error" ,
160
- "Directory %r of parameter 'output_dir' does not exist and could not be created." % parameters ['output_dir' ]]]
204
+ return [
205
+ [
206
+ "error" ,
207
+ "Directory %r of parameter 'output_dir' does not exist and could not be created."
208
+ % parameters ["output_dir" ],
209
+ ]
210
+ ]
161
211
else :
162
212
if created :
163
- return [["info" ,
164
- "Directory %r of parameter 'output_dir' did not exist, but has now been created."
165
- "" % parameters ['output_dir' ]]]
213
+ return [
214
+ [
215
+ "info" ,
216
+ "Directory %r of parameter 'output_dir' did not exist, but has now been created."
217
+ "" % parameters ["output_dir" ],
218
+ ]
219
+ ]
166
220
167
221
168
222
BOT = MISPFeedOutputBot
0 commit comments