stix-shifter-modules-sysdig 8.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. stix_shifter_modules/sysdig/__init__.py +0 -0
  2. stix_shifter_modules/sysdig/configuration/config.json +603 -0
  3. stix_shifter_modules/sysdig/configuration/dialects.json +6 -0
  4. stix_shifter_modules/sysdig/configuration/lang_en.json +69 -0
  5. stix_shifter_modules/sysdig/entry_point.py +12 -0
  6. stix_shifter_modules/sysdig/stix_translation/__init__.py +0 -0
  7. stix_shifter_modules/sysdig/stix_translation/json/config_map.json +33 -0
  8. stix_shifter_modules/sysdig/stix_translation/json/from_stix_map.json +110 -0
  9. stix_shifter_modules/sysdig/stix_translation/json/operators.json +13 -0
  10. stix_shifter_modules/sysdig/stix_translation/json/stix_2_1/from_stix_map.json +110 -0
  11. stix_shifter_modules/sysdig/stix_translation/json/stix_2_1/to_stix_map.json +332 -0
  12. stix_shifter_modules/sysdig/stix_translation/json/to_stix_map.json +332 -0
  13. stix_shifter_modules/sysdig/stix_translation/json_to_stix_translator.py +529 -0
  14. stix_shifter_modules/sysdig/stix_translation/query_constructor.py +472 -0
  15. stix_shifter_modules/sysdig/stix_translation/query_translator.py +26 -0
  16. stix_shifter_modules/sysdig/stix_translation/transformers.py +66 -0
  17. stix_shifter_modules/sysdig/stix_transmission/__init__.py +0 -0
  18. stix_shifter_modules/sysdig/stix_transmission/api_client.py +37 -0
  19. stix_shifter_modules/sysdig/stix_transmission/connector.py +213 -0
  20. stix_shifter_modules/sysdig/stix_transmission/error_mapper.py +34 -0
  21. stix_shifter_modules_sysdig-8.0.2.dist-info/METADATA +148 -0
  22. stix_shifter_modules_sysdig-8.0.2.dist-info/RECORD +27 -0
  23. stix_shifter_modules_sysdig-8.0.2.dist-info/WHEEL +5 -0
  24. stix_shifter_modules_sysdig-8.0.2.dist-info/licenses/AUTHORS.md +23 -0
  25. stix_shifter_modules_sysdig-8.0.2.dist-info/licenses/LICENSE.md +219 -0
  26. stix_shifter_modules_sysdig-8.0.2.dist-info/licenses/NOTICE +32 -0
  27. stix_shifter_modules_sysdig-8.0.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,472 @@
1
+ """ This Module will convert Stix Pattern to Sysdig data source supported query """
2
+ import re
3
+ import json
4
+ import logging
5
+ from os import path
6
+ from datetime import datetime, timedelta
7
+ from stix_shifter_utils.stix_translation.src.patterns.pattern_objects import ObservationExpression, \
8
+ ComparisonExpression, ComparisonComparators, Pattern, \
9
+ CombinedComparisonExpression, CombinedObservationExpression
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ START_STOP_PATTERN = r"(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z)"
14
+ MAC = '^(([0-9a-fA-F]{2}[:-]){5}([0-9a-fA-F]{2}))$'
15
+ TYPE_MAP_PATH = "json/config_map.json"
16
+
17
+ STOP_TIME = datetime.utcnow()
18
+
19
+
20
+ class StartStopQualifierValueException(Exception):
21
+ """ Start Stop qualifier exception """
22
+ pass
23
+
24
+
25
+ class FileNotFoundException(Exception):
26
+ """ file not found exception """
27
+ pass
28
+
29
+
30
+ class QueryStringPatternTranslator:
31
+ """
32
+ translate stix pattern to native data source query language
33
+ """
34
+
35
+ def __init__(self, pattern: Pattern, data_model_mapper, options):
36
+ logger.info("Sysdig Connector")
37
+ self.dmm = data_model_mapper
38
+ self.options = options
39
+ self.comparator_lookup = self.dmm.map_comparator()
40
+ self.type_map = self.load_json(TYPE_MAP_PATH)
41
+ self.translated_query = self.parse_expression(pattern)
42
+
43
+ @staticmethod
44
+ def load_json(rel_path_of_file):
45
+ """
46
+ Consumes a json file and returns a dictionary
47
+ :param rel_path_of_file: str
48
+ :return: dict
49
+ """
50
+ _json_path = path.dirname(path.realpath(__file__)) + "/" + rel_path_of_file
51
+ try:
52
+ if path.exists(_json_path):
53
+ with open(_json_path, encoding='utf-8') as f_obj:
54
+ return json.load(f_obj)
55
+ raise FileNotFoundException
56
+ except FileNotFoundException as ex:
57
+ raise FileNotFoundError(f'{rel_path_of_file} not found') from ex
58
+
59
+ def _format_set(self, values, mapped_field_type, mapped_fields_array):
60
+ """
61
+ Formats value in the event of set operation
62
+ :param values
63
+ :param mapped_field_type: str
64
+ :param mapped_fields_array: list
65
+ :return formatted value
66
+ """
67
+ gen = values.element_iterator()
68
+ formatted_value = ','.join(QueryStringPatternTranslator._escape_value(
69
+ self._format_value_type(value, mapped_field_type, mapped_fields_array), mapped_field_type)
70
+ for value in gen)
71
+ return f'({formatted_value})'
72
+
73
+ @staticmethod
74
+ def _format_equality(value, mapped_field_type):
75
+ """
76
+ Formats value in the event of equality operation
77
+ :param value
78
+ :return formatted value
79
+ """
80
+ return QueryStringPatternTranslator._escape_value(value, mapped_field_type)
81
+
82
+ @staticmethod
83
+ def _escape_value(value, mapped_field_type):
84
+ """
85
+ adds escape characters to string type value
86
+ :param value
87
+ :return formatted value
88
+ """
89
+ if mapped_field_type == "int":
90
+ return value
91
+ if isinstance(value, str):
92
+ value = f'\"{value}\"'
93
+ return str(value)
94
+
95
+ @staticmethod
96
+ def _field_severity(value):
97
+ """
98
+ convert severity 1-100 to data source value 0-7
99
+ High: 0-3, Medium: 4-5, Low: 6, Info: 7
100
+ param value
101
+ return value(int)
102
+ examples:
103
+ severity is 100, after conversion is 0.
104
+ severity is 30, after conversion is 7.
105
+ Reference :
106
+ Sysdig Docs https://docs.sysdig.com/en/docs/sysdig-secure/secure-events/event-forwarding/#policy-event-severity
107
+ """
108
+ value = int(value)
109
+ if 91 <= value <= 100:
110
+ value = 0
111
+ elif 81 <= value <= 90:
112
+ value = 1
113
+ elif 71 <= value <= 80:
114
+ value = 2
115
+ elif 61 <= value <= 70:
116
+ value = 3
117
+ elif 51 <= value <= 60:
118
+ value = 4
119
+ elif 41 <= value <= 50:
120
+ value = 5
121
+ elif 31 <= value <= 40:
122
+ value = 6
123
+ elif 1 <= value <= 30:
124
+ value = 7
125
+ else:
126
+ raise NotImplementedError('only 1-100 integer values are supported with this field')
127
+ return value
128
+
129
+ def _format_value_type(self, value, mapped_field_type, mapped_fields_array):
130
+ """
131
+ check input value format that matches with the mapped field value type
132
+ :param value
133
+ :param mapped_field_type: str
134
+ :param mapped_fields_array: list
135
+ :return formatted value
136
+ """
137
+ converted_value = str(value)
138
+ if mapped_field_type == "mac":
139
+ compile_mac_regex = re.compile(MAC)
140
+ if not compile_mac_regex.search(converted_value):
141
+ raise NotImplementedError(f'Invalid mac address - {converted_value} provided')
142
+ elif mapped_field_type == "int":
143
+ if not converted_value.isdigit():
144
+ raise NotImplementedError(f'string type input - {converted_value} is not supported for '
145
+ f'integer type fields')
146
+ if mapped_fields_array[0] == "severity":
147
+ value = self._field_severity(value)
148
+ converted_value = str(value)
149
+ return converted_value
150
+
151
+ def _check_value_comparator_support(self, comparator, mapped_field_type):
152
+ """
153
+ checks the comparator and value support
154
+ :param comparator
155
+ :param mapped_field_type: str
156
+ """
157
+ operator = self.comparator_lookup[str(comparator)]
158
+
159
+ if mapped_field_type == 'string' and comparator not in [ComparisonComparators.In,
160
+ ComparisonComparators.Equal,
161
+ ComparisonComparators.NotEqual
162
+ ]:
163
+ raise NotImplementedError(f'{operator} operator is not supported for string type input')
164
+ if mapped_field_type == 'int' and comparator not in [ComparisonComparators.In,
165
+ ComparisonComparators.Equal,
166
+ ComparisonComparators.NotEqual,
167
+ ComparisonComparators.GreaterThan,
168
+ ComparisonComparators.GreaterThanOrEqual,
169
+ ComparisonComparators.LessThanOrEqual,
170
+ ComparisonComparators.LessThan]:
171
+ raise NotImplementedError(f'{operator} operator is not supported for integer type input')
172
+
173
+ @staticmethod
174
+ def _format_negate(comparator):
175
+ """
176
+ returns negation of input operator
177
+ :param comparator:str
178
+ :return str
179
+ """
180
+ negate_comparator = {
181
+ ">": "<=",
182
+ ">=": "<",
183
+ "<": ">=",
184
+ "<=": ">",
185
+ "=": "!=",
186
+ "in": "not in"
187
+ }
188
+ return negate_comparator[comparator]
189
+
190
+ @staticmethod
191
+ def _check_time_range_values(from_epoch, to_epoch):
192
+ """
193
+ checks for valid start and stop time
194
+ :param from_epoch: int
195
+ :param to_epoch: int
196
+ """
197
+ if from_epoch > to_epoch:
198
+ raise StartStopQualifierValueException('Start time should be lesser than Stop time')
199
+
200
+ no_of_days = (to_epoch - from_epoch)/86400000000000 # converting nano seconds to days
201
+ max_day = 14 # time range maximum up to 14 days
202
+ if max_day < no_of_days:
203
+ logger.warning('Sysdig connector query time range can be maximum up to 14 days')
204
+
205
+ @staticmethod
206
+ def _parse_time_range(qualifier, time_range):
207
+ """
208
+ Converts qualifier timestamp to epoch
209
+ :param qualifier: str
210
+ :param time_range: int
211
+ return: list of converted epoch values
212
+ """
213
+ try:
214
+ compile_timestamp_regex = re.compile(START_STOP_PATTERN)
215
+ if qualifier and compile_timestamp_regex.search(qualifier):
216
+ time_range_iterator = compile_timestamp_regex.finditer(qualifier)
217
+ time_range_list = [each.group() for each in time_range_iterator]
218
+ else:
219
+ start_time = STOP_TIME - timedelta(minutes=time_range)
220
+ converted_start_time = start_time.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z'
221
+ # limit 3 digit value for millisecond
222
+ converted_stop_time = STOP_TIME.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z'
223
+ time_range_list = [converted_start_time, converted_stop_time]
224
+ except (KeyError, IndexError, TypeError) as ex:
225
+ raise ex
226
+ return time_range_list
227
+
228
+ @staticmethod
229
+ def get_epoch_time(timestamp):
230
+ """
231
+ Converting timestamp (YYYY-MM-DDThh:mm:ss.000Z) to 13-digit Unix time (epoch + milliseconds)
232
+ :param timestamp: str, timestamp
233
+ :return: int, epoch time
234
+ """
235
+ time_patterns = ['%Y-%m-%dT%H:%M:%SZ', '%Y-%m-%dT%H:%M:%S.%fZ']
236
+ epoch = datetime(1970, 1, 1)
237
+ for time_pattern in time_patterns:
238
+ try:
239
+ converted_time = int(
240
+ ((datetime.strptime(timestamp, time_pattern) - epoch).total_seconds()) * 1000 * 1000000)
241
+ return converted_time
242
+ except ValueError:
243
+ pass
244
+ raise NotImplementedError("cannot convert the timestamp {} to epoch time".format(timestamp))
245
+
246
+ def _add_timestamp_to_query(self, query, qualifier):
247
+ """
248
+ adds timestamp filter to Sysdig query
249
+ :param query: str
250
+ :param qualifier
251
+ :return str
252
+ """
253
+ converted_timestamp = \
254
+ QueryStringPatternTranslator._parse_time_range(qualifier, self.options["time_range"])
255
+ from_epoch = self.get_epoch_time(converted_timestamp[0])
256
+ to_epoch = self.get_epoch_time(converted_timestamp[1])
257
+ QueryStringPatternTranslator._check_time_range_values(from_epoch, to_epoch)
258
+ final_query = f'from={from_epoch}&to={to_epoch}&filter={query}'
259
+ return final_query
260
+
261
+ def _check_mapped_field_type(self, mapped_field_array):
262
+ """
263
+ Returns the type of mapped field array
264
+ :param mapped_field_array: list
265
+ :return: str
266
+ """
267
+ mapped_field = mapped_field_array[0]
268
+ mapped_field_type = "string"
269
+ for key, value in self.type_map.items():
270
+ if mapped_field in value and key in ["int_supported_fields", "string_supported_fields",
271
+ "mac_supported_fields"]:
272
+ mapped_field_type = key.split('_')[0]
273
+ break
274
+ return mapped_field_type
275
+
276
+ @staticmethod
277
+ def _parse_mapped_fields(value, comparator, mapped_fields_array):
278
+ """
279
+ parse mapped fields into query expression
280
+ :param value: str
281
+ :param comparator: str
282
+ :param mapped_fields_array: list
283
+ :return: str
284
+ """
285
+ comparison_string = ""
286
+ mapped_fields_count = len(mapped_fields_array)
287
+ for mapped_field in mapped_fields_array:
288
+ if "in" in comparator:
289
+ comparison_string += f'{mapped_field} {comparator} {value}'
290
+ else:
291
+ comparison_string += f'{mapped_field}{comparator}{value}'
292
+ if mapped_fields_count > 1:
293
+ comparison_string += " OR "
294
+ mapped_fields_count -= 1
295
+ return comparison_string
296
+
297
+ def _lookup_comparison_operator(self, expression_operator):
298
+ """
299
+ lookup operators support in Sysdig connector
300
+ :param expression_operator:enum object
301
+ :return str
302
+ """
303
+ if str(expression_operator) not in self.comparator_lookup:
304
+ raise NotImplementedError(
305
+ f'Comparison operator {expression_operator.name} unsupported for Sysdig connector')
306
+ operator = self.comparator_lookup[str(expression_operator)]
307
+ return operator
308
+
309
+ def _eval_comparison_value(self, expression, mapped_field_type, mapped_fields_array):
310
+ """
311
+ Function for parsing comparison expression value
312
+ :param expression: expression object
313
+ :param mapped_field_type:str
314
+ :param mapped_fields_array: list
315
+ :return: formatted expression value
316
+ """
317
+ if expression.comparator == ComparisonComparators.In:
318
+ value = self._format_set(expression.value, mapped_field_type, mapped_fields_array)
319
+ elif expression.comparator not in [ComparisonComparators.Like, ComparisonComparators.Matches]:
320
+ value = self._format_value_type(expression.value, mapped_field_type, mapped_fields_array)
321
+ self._check_value_comparator_support(expression.comparator, mapped_field_type)
322
+ value = self._format_equality(value, mapped_field_type)
323
+ else:
324
+ raise NotImplementedError('Unknown comparator expression operator')
325
+ return value
326
+
327
+ def _eval_combined_comparison_exp(self, expression):
328
+ """
329
+ Function for parsing combined comparison expression
330
+ :param expression: expression object
331
+ """
332
+ operator = self._lookup_comparison_operator(expression.operator)
333
+ expression_01 = self._parse_expression(expression.expr1)
334
+ expression_02 = self._parse_expression(expression.expr2)
335
+ if not expression_01 or not expression_02:
336
+ return ''
337
+ if isinstance(expression.expr1, CombinedComparisonExpression):
338
+ expression_01 = f'{expression_01}'
339
+ if isinstance(expression.expr2, CombinedComparisonExpression):
340
+ expression_02 = f'{expression_02}'
341
+
342
+ query_string = f'{expression_01}{operator}{expression_02}'
343
+ return f'{query_string}'
344
+
345
+ def _eval_combined_observation_exp(self, expression, qualifier=None):
346
+ """
347
+ Function for parsing combined observation expression
348
+ :param expression: expression object
349
+ :param qualifier: qualifier
350
+ """
351
+ operator = self._lookup_comparison_operator(expression.operator)
352
+ expression_01 = self._parse_expression(expression.expr1, qualifier)
353
+ expression_02 = self._parse_expression(expression.expr2, qualifier)
354
+ query = ''
355
+ if expression_01 and expression_02:
356
+ query = f'{expression_01} {operator} {expression_02}'
357
+
358
+ elif expression_01:
359
+ query = f'{expression_01}'
360
+ elif expression_02:
361
+ query = f'{expression_02}'
362
+ return query
363
+
364
+ def _parse_expression(self, expression, qualifier=None):
365
+ """
366
+ parse ANTLR pattern to Sysdig native query
367
+ :param expression: expression object, ANTLR parsed expression object
368
+ :param qualifier: str, default in None
369
+ :return str
370
+ """
371
+ if isinstance(expression, ComparisonExpression): # Base Case
372
+ stix_objects = expression.object_path.split(':')
373
+ mapped_fields_array = self.dmm.map_field(stix_objects[0], stix_objects[1])
374
+ comparator = self._lookup_comparison_operator(expression.comparator)
375
+ if expression.negated:
376
+ comparator = QueryStringPatternTranslator._format_negate(comparator)
377
+ mapped_field_type = self._check_mapped_field_type(mapped_fields_array)
378
+ value = self._eval_comparison_value(expression, mapped_field_type, mapped_fields_array)
379
+ comparison_string = self._parse_mapped_fields(value, comparator, mapped_fields_array)
380
+ return comparison_string
381
+
382
+ elif isinstance(expression, CombinedComparisonExpression):
383
+ return self._eval_combined_comparison_exp(expression)
384
+
385
+ elif isinstance(expression, ObservationExpression):
386
+ query_string = self._parse_expression(expression.comparison_expression)
387
+ query_string = self._add_timestamp_to_query(query_string, qualifier)
388
+ return query_string
389
+
390
+ elif hasattr(expression, 'qualifier') and hasattr(expression, 'observation_expression'):
391
+ if isinstance(expression.observation_expression, CombinedObservationExpression):
392
+ operator = self._lookup_comparison_operator(expression.observation_expression.operator)
393
+ expression_01 = self._parse_expression(expression.observation_expression.expr1,
394
+ expression.qualifier)
395
+ expression_02 = self._parse_expression(expression.observation_expression.expr2,
396
+ expression.qualifier)
397
+ query_string = f'{expression_01} {operator} {expression_02}'
398
+ else:
399
+ query_string = self._parse_expression(expression.observation_expression,
400
+ expression.qualifier)
401
+ if qualifier is not None:
402
+ query_string = self._add_timestamp_to_query(query_string, qualifier)
403
+ return query_string
404
+
405
+ elif isinstance(expression, CombinedObservationExpression):
406
+
407
+ return self._eval_combined_observation_exp(expression, qualifier)
408
+
409
+ elif isinstance(expression, Pattern):
410
+ return self._parse_expression(expression.expression)
411
+ else:
412
+ raise RuntimeError(f'Unknown Recursion Case for expression={expression},'
413
+ f' type(expression)={type(expression)}')
414
+
415
+ def parse_expression(self, pattern: Pattern):
416
+ """
417
+ Conversion of ANTLR pattern to Sysdig query
418
+ :param pattern: expression object, ANTLR parsed expression object
419
+ :return: str, native query
420
+ """
421
+ return self._parse_expression(pattern)
422
+
423
+ @staticmethod
424
+ def format_multiple_observations(query):
425
+ """
426
+ timestamp formatting for multiple observations
427
+ """
428
+ split_queries = query.split(' or ')
429
+ query_dict = {}
430
+ for row in split_queries:
431
+ # making single time stamp instead of having multiple same time stamps for multiple queries
432
+ split_query = row.split('filter=')
433
+ query_timestamp = split_query[0]
434
+ if query_dict.get(query_timestamp):
435
+ query_dict[query_timestamp] += " or " + split_query[1]
436
+ else:
437
+ query_dict[query_timestamp] = 'filter=' + split_query[1]
438
+ final_query = [key + value for key, value in query_dict.items()]
439
+ return final_query
440
+
441
+ @staticmethod
442
+ def removing_audit_trail_logs(format_queries):
443
+ """
444
+ Displays error message when searched for auditTrail events.
445
+ Adds the condition to filter auditTrail events for other queries.
446
+ """
447
+ query_list = []
448
+ remove_audit = "andsource!=\"auditTrail\""
449
+ if any('auditTrail' in query for query in format_queries):
450
+ raise NotImplementedError('Sysdig connector does not provide auditTrail event')
451
+ else:
452
+ for query in format_queries:
453
+ split_format_query = query.split('filter=')
454
+ query_list.append(f'{split_format_query[0]}filter=({split_format_query[1]}){remove_audit}')
455
+ return query_list
456
+
457
+ def translate_pattern(pattern: Pattern, data_model_mapping, options):
458
+ """
459
+ Conversion of ANTLR pattern to native data source query
460
+ :param pattern: expression object, ANTLR parsed expression object
461
+ :param data_model_mapping: DataMapper object, mapping object obtained by parsing json
462
+ :param options: dict, time_range defaults to 5
463
+ :return: string, Sysdig queries
464
+ """
465
+ translated_query_strings = QueryStringPatternTranslator(pattern, data_model_mapping, options)
466
+ final_queries = translated_query_strings.translated_query
467
+
468
+ if final_queries.count('from') > 1:
469
+ final_queries = QueryStringPatternTranslator.format_multiple_observations(final_queries)
470
+ return QueryStringPatternTranslator.removing_audit_trail_logs(final_queries)
471
+ else:
472
+ return QueryStringPatternTranslator.removing_audit_trail_logs([final_queries])
@@ -0,0 +1,26 @@
1
+ import logging
2
+
3
+ from stix_shifter_utils.modules.base.stix_translation.base_query_translator import BaseQueryTranslator
4
+ from . import query_constructor
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+
9
+ class QueryTranslator(BaseQueryTranslator):
10
+
11
+ def transform_antlr(self, data, antlr_parsing_object):
12
+ """
13
+ Transforms STIX pattern into a different query format. Based on a mapping file
14
+ :param antlr_parsing_object: Antlr parsing objects for the STIX pattern
15
+ :type antlr_parsing_object: object
16
+ :param mapping: The mapping file path to use as instructions on how to transform the given STIX query into another format. This should default to something if one isn't passed in
17
+ :type mapping: str (filepath)
18
+ :return: transformed query string
19
+ :rtype: str
20
+ """
21
+
22
+ logger.info("Converting STIX2 Pattern to data source query")
23
+
24
+ query_string = query_constructor.translate_pattern(
25
+ antlr_parsing_object, self, self.options)
26
+ return query_string
@@ -0,0 +1,66 @@
1
+ from stix_shifter_utils.stix_translation.src.utils.transformers import ValueTransformer
2
+ from stix_shifter_utils.utils import logger
3
+ import re
4
+ from datetime import datetime
5
+
6
+ LOGGER = logger.set_logger(__name__)
7
+ connector = __name__.split('.')[1]
8
+
9
+
10
+ class TimestampConversion(ValueTransformer):
11
+ """ Convert timezone date to timestamp (YYYY-MM-DDThh:mm:ss.000Z)"""
12
+
13
+ @staticmethod
14
+ def transform(timestamp):
15
+ try:
16
+ # with milliseconds
17
+ if re.search(r"\d{4}(-\d{2}){2} \d{2}(:\d{2}){2}.\d{0,6}\+\d{2}:\d{2}", str(timestamp)):
18
+ converted_date = datetime.strptime(timestamp, "%Y-%m-%d %H:%M:%S.%f%z")
19
+ timestamp = datetime.strftime(converted_date, "%Y-%m-%dT%H:%M:%S.%fZ")
20
+ # for without milliseconds, setting three zeroes in the millisecond in the converted date
21
+ elif re.search(r"\d{4}(-\d{2}){2} \d{2}(:\d{2}){2}\+\d{2}:\d{2}", str(timestamp)):
22
+ converted_date = datetime.strptime(timestamp, "%Y-%m-%d %H:%M:%S%z")
23
+ timestamp = datetime.strftime(converted_date, "%Y-%m-%dT%H:%M:%S.%f")[:-3] + 'Z'
24
+ except:
25
+ LOGGER.error(f'{connector} connector error cannot convert this timestamp %s', timestamp)
26
+ return timestamp
27
+
28
+
29
+ class SeverityToScore(ValueTransformer):
30
+
31
+ @staticmethod
32
+ def transform(severity):
33
+ """
34
+ sysdig severity values levels 0-7 :
35
+ High: 0-3, Medium: 4-5, Low: 6, Info: 7
36
+ converting sysdig severity values 0-7 to 1-100
37
+ Example :
38
+ High: sysdig severity : 0, after conversion : 100
39
+ Low : sysdig severity : 6, after conversion : 40
40
+ Reference :
41
+ Sysdig Docs https://docs.sysdig.com/en/docs/sysdig-secure/secure-events/event-forwarding/#policy-event-severity
42
+ """
43
+ try:
44
+ # value transformer to convert severity value on a scale of 1-100
45
+ if isinstance(severity, int):
46
+ return (10 - int(severity)) * 10
47
+ return severity
48
+ except KeyError:
49
+ LOGGER.error(f'{connector} connector error cannot convert severity scale value')
50
+
51
+
52
+ class HostnameToIpAddress(ValueTransformer):
53
+ """
54
+ Converts Node Name into IP address
55
+ """
56
+ @staticmethod
57
+ def transform(address):
58
+ try:
59
+ match = re.search(r'\d+\-\d+\-\d+\-\d+', address)
60
+ if match:
61
+ # Getting the matched IP address
62
+ extracted_ip = match.group()
63
+ return extracted_ip.replace('-', '.')
64
+ return address
65
+ except KeyError:
66
+ LOGGER.error(f'{connector} connector error cannot convert ip value')
@@ -0,0 +1,37 @@
1
+ from stix_shifter_utils.stix_transmission.utils.RestApiClientAsync import RestApiClientAsync
2
+
3
+
4
+ class APIClient:
5
+ PING_ENDPOINT = 'api/v1/secureEvents/status'
6
+ EVENTS_ENDPOINT = 'api/v1/secureEvents?'
7
+
8
+ def __init__(self, connection, configuration):
9
+ self.auth = configuration.get('auth')
10
+ self.result_limit = connection['options'].get("result_limit")
11
+ headers = {}
12
+ if 'token' in self.auth:
13
+ headers['Authorization'] = "Bearer " + self.auth.get('token')
14
+ headers["Accept"] = "application/json"
15
+ headers["Content-Type"] = "application/json;charset=UTF-8"
16
+ # Added self-signed certificate parameter for verification
17
+ self.client = RestApiClientAsync(connection.get('host'),
18
+ connection.get('port', None),
19
+ headers,
20
+ url_modifier_function=None,
21
+ cert_verify=connection.get('selfSignedCert'))
22
+ self.timeout = connection['options'].get('timeout')
23
+
24
+ async def ping_data_source(self):
25
+ """
26
+ ping the Data Source
27
+ :return: Response object
28
+ """
29
+ return await self.client.call_api(self.PING_ENDPOINT, 'GET', headers=self.client.headers, timeout=self.timeout)
30
+
31
+ async def get_search_results(self, query):
32
+ """
33
+ :param query: Data Source Query
34
+ :return: Response Object
35
+ """
36
+ return await self.client.call_api(self.EVENTS_ENDPOINT, 'GET', urldata=query,
37
+ headers=self.client.headers, timeout=self.timeout)