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,529 @@
1
+ from os import path
2
+ import uuid
3
+ import json
4
+ import regex as re
5
+
6
+ from stix_shifter_utils.utils.helpers import dict_merge
7
+ from stix_shifter_utils.stix_translation.src.json_to_stix import observable, id_contributing_properties
8
+ from datetime import datetime
9
+ from stix_shifter_utils.utils import logger
10
+ from stix_shifter_utils.utils.helpers import StixObjectId
11
+
12
+ # "ID Contributing Properties" taken from https://docs.oasis-open.org/cti/stix/v2.1/csprd01/stix-v2.1-csprd01.html#_Toc16070594
13
+ UUID5_NAMESPACE = "00abedb4-aa42-466c-9c01-fed23315a9b7"
14
+ NUMBER_OBSERVED_KEY = 'number_observed'
15
+ FIRST_OBSERVED_KEY = 'first_observed'
16
+ LAST_OBSERVED_KEY = 'last_observed'
17
+
18
+
19
+ # convert JSON data to STIX object using map_data and transformers
20
+ def convert_to_stix(data_source, map_data, data, transformers, options, callback=None):
21
+ try:
22
+ ds2stix = DataSourceObjToStixObj(data_source, map_data, transformers, options, callback)
23
+
24
+ # map data list to list of transformed objects
25
+ observation = ds2stix.transform
26
+ results = list(map(observation, data))
27
+
28
+ for stix_object in results:
29
+ if ds2stix.spec_version == "2.1":
30
+ del stix_object["objects"]
31
+ ds2stix.bundle["objects"].append(stix_object)
32
+
33
+ for _, value in ds2stix.unique_cybox_objects.items():
34
+ ds2stix.bundle["objects"].append(value)
35
+
36
+ return ds2stix.bundle
37
+
38
+ except Exception as e:
39
+ try:
40
+ # try to print the error line
41
+ logger_log = logger.set_logger(__name__)
42
+ logger_log.error(logger.last_tb_to_string(e))
43
+ except:
44
+ pass
45
+ raise e
46
+
47
+ class DataSourceObjToStixObj:
48
+ logger = logger.set_logger(__name__)
49
+
50
+ def __init__(self, data_source, ds_to_stix_map, transformers, options, callback=None):
51
+ self.identity_id = data_source["id"]
52
+ self.ds_to_stix_map = ds_to_stix_map
53
+ self.transformers = transformers
54
+ self.options = options
55
+ self.callback = callback
56
+
57
+ # parse through options
58
+
59
+ self.properties = observable.properties
60
+
61
+ self.data_source = data_source['name']
62
+ self.ds_key_map = [val for val in self.gen_dict_extract('ds_key', ds_to_stix_map)]
63
+
64
+ self.bundle = {
65
+ "type": "bundle",
66
+ "id": "bundle--" + str(uuid.uuid4()),
67
+ "objects": []
68
+ }
69
+
70
+ if options.get("stix_2.1"):
71
+ self.spec_version = "2.1"
72
+ self.contributing_properties_definitions = id_contributing_properties.properties
73
+ else:
74
+ self.spec_version = "2.0"
75
+ self.bundle["spec_version"] = "2.0"
76
+ self.unique_cybox_objects = {}
77
+ self.bundle['objects'] += [data_source]
78
+
79
+
80
+ # get the nested ds_keys in the mapping
81
+ def gen_dict_extract(self, key, var):
82
+ if hasattr(var, 'items'):
83
+ for k, v in var.items():
84
+ if k == key:
85
+ yield v
86
+ if isinstance(v, dict):
87
+ for result in self.gen_dict_extract(key, v):
88
+ yield result
89
+ elif isinstance(v, list):
90
+ for d in v:
91
+ for result in self.gen_dict_extract(key, d):
92
+ yield result
93
+
94
+
95
+ def _valid_stix_value(self, observable_key, stix_value):
96
+ """
97
+ Checks that the given STIX value is valid for this STIX property
98
+ :param observable_key: the STIX property name
99
+ :param stix_value: the STIX value translated from the input object
100
+ :return: whether STIX value is valid for this STIX property
101
+ :rtype: bool
102
+ """
103
+ try:
104
+ if observable_key in self.properties and 'valid_regex' in self.properties[observable_key]:
105
+ pattern = re.compile(self.properties[observable_key]['valid_regex'])
106
+ match = pattern.match
107
+ if not match(str(stix_value)):
108
+ return False
109
+ return True
110
+ except Exception as e:
111
+ self.logger.debug("Failed to validate STIX property '{}' with value '{}'. Exception: {}".format(observable_key, stix_value, e))
112
+ return False
113
+
114
+ def _compose_value_object(self, value, key_list, observable_key=None, object_tag_ref_map=None, transformer=None, references=None, unwrap=False, is_group_ref=False):
115
+ """
116
+ Converts the value of the data to STIX valid value
117
+ """
118
+ try:
119
+ return_value = {}
120
+ for key in key_list:
121
+ return_value[key] = self._compose_value_object(value, key_list[1:], observable_key=observable_key, object_tag_ref_map=object_tag_ref_map, transformer=transformer, references=references, unwrap=unwrap, is_group_ref=is_group_ref)
122
+ break
123
+ else:
124
+ if transformer:
125
+ try:
126
+ value = transformer.transform(value)
127
+ if value is None:
128
+ return None
129
+ except Exception:
130
+ return None
131
+
132
+ if references:
133
+ if isinstance(references, list):
134
+ return_value = []
135
+ for ref in references:
136
+ if not isinstance(value, list):
137
+ # Fetch the index of reference object name which has a single value.
138
+ parent_key_ind = self._get_tag_ind(ref, object_tag_ref_map, create_on_absence=False)
139
+ if parent_key_ind:
140
+ return_value.append(parent_key_ind)
141
+ else:
142
+ for i, _ in enumerate(value):
143
+ parent_key_ind = self._get_tag_ind(ref, object_tag_ref_map, create_on_absence=False, unwrap=str(i))
144
+ if parent_key_ind:
145
+ return_value.append(parent_key_ind)
146
+ # Iterate through the object_tag_ref_map to fetch the index, for grouping
147
+ # reference objects of nested type through group_ref.
148
+ elif is_group_ref:
149
+ list_parent_ind = ref + '_' + str(i)
150
+ for key, val in object_tag_ref_map['tags'].items():
151
+ if key.startswith(list_parent_ind):
152
+ return_value.append(
153
+ self._get_tag_ind(key, object_tag_ref_map, create_on_absence=False))
154
+
155
+ else:
156
+ # Fetch first object (index 0) for single reference when the value is of type list .
157
+ if isinstance(value, list):
158
+ return_value = self._get_tag_ind(references, object_tag_ref_map, create_on_absence=False,
159
+ unwrap='0')
160
+ else:
161
+ return_value = self._get_tag_ind(references, object_tag_ref_map, create_on_absence=False)
162
+ # if the property has unwrap true and is not a list, convert to list
163
+ if unwrap is True and not isinstance(return_value, list):
164
+ return_value = [return_value]
165
+
166
+ if not return_value:
167
+ return None
168
+ else:
169
+ if unwrap is False and observable_key and not self._valid_stix_value(observable_key, value):
170
+ return None
171
+ return_value = value
172
+
173
+ return return_value
174
+ except Exception as e:
175
+ raise Exception("Error in json_to_stix_translator._compose_value_object: %s" % e)
176
+
177
+
178
+ def _get_tag_ind(self, tag, object_tag_ref_map, create_on_absence=False, unwrap=False, property_key=None):
179
+ """
180
+ Gets the stringified index of the observable object from the `object_tag_ref_map` cached dictionary if it exists
181
+ or creates otherwise.
182
+ """
183
+ tag_ind = None
184
+ tag_ind_str = None
185
+ try:
186
+ # if the datasource fields is a collection of json object than we need to unwrap it and create multiple objects
187
+ if unwrap:
188
+ tag = tag + '_' + str(unwrap)
189
+
190
+ if tag in object_tag_ref_map['tags']:
191
+ tag_ind = object_tag_ref_map['tags'][tag]['i']
192
+ object_tag_ref_map['tags'][tag]['n'] += 1
193
+ elif create_on_absence:
194
+ tag_ind_str = str(object_tag_ref_map[UUID5_NAMESPACE])
195
+ if self.spec_version == "2.1":
196
+ tag_ind = StixObjectId(tag_ind_str)
197
+ else:
198
+ tag_ind = tag_ind_str
199
+
200
+ object_tag_ref_map[UUID5_NAMESPACE] += 1
201
+ object_tag_ref_map['tags'][tag] = {'i': tag_ind, 'n': 0}
202
+
203
+ if tag_ind is not None:
204
+ if not tag_ind_str:
205
+ tag_ind_str = str(tag_ind)
206
+ if property_key and '_ref' not in property_key:
207
+ object_tag_ref_map['non_ref_props'][tag_ind_str] = True
208
+ except Exception as e:
209
+ raise Exception("Error in json_to_stix_translator._get_tag_ind: %s : %s" % (e, e.__traceback__.tb_lineno))
210
+
211
+ return tag_ind
212
+
213
+ def _add_property(self, type_name, property_key, parent_key_ind, value, objects, group=False, cybox=True):
214
+ """
215
+ Add observable object property and its STIX valid value to the cached `objects` dictionary
216
+ """
217
+ named_group = isinstance(group, str) and group.lower() != "true"
218
+ parent_key_ind_str = str(parent_key_ind)
219
+ if not parent_key_ind_str in objects:
220
+ if cybox:
221
+ # Grouped properties go in a list
222
+ if named_group:
223
+ value = [value]
224
+ objects[parent_key_ind_str] = {
225
+ 'type': type_name,
226
+ property_key: value
227
+ }
228
+ if self.spec_version == "2.1":
229
+ objects[parent_key_ind_str]["id"] = parent_key_ind
230
+ objects[parent_key_ind_str]["spec_version"] = "2.1"
231
+ else:
232
+ objects[parent_key_ind_str] = {
233
+ property_key: value
234
+ }
235
+ else:
236
+ if not property_key in objects[parent_key_ind_str]:
237
+ objects[parent_key_ind_str][property_key] = value
238
+ # Add grouped value in existing list element
239
+ elif isinstance(value, dict) and named_group and isinstance(objects[parent_key_ind_str][property_key], list):
240
+ objects[parent_key_ind_str][property_key][0] = dict_merge(objects[parent_key_ind_str][property_key][0], value)
241
+ elif isinstance(value, dict):
242
+ objects[parent_key_ind_str][property_key] = dict_merge(objects[parent_key_ind_str][property_key], value)
243
+ elif isinstance(objects[parent_key_ind_str][property_key], list) and group:
244
+ objects[parent_key_ind_str][property_key].extend(value)
245
+
246
+
247
+ def _handle_properties(self, to_stix_config_prop, data, objects, object_tag_ref_map, parent_data=None, ds_sub_key=None, object_key_ind=None):
248
+ """
249
+ Walks through data object, matches the property names of the data elements with the to_stix_map property names and
250
+ send the values to process if the data is the final value
251
+ """
252
+ try:
253
+ if data is not None:
254
+ if isinstance(to_stix_config_prop, dict) and to_stix_config_prop.get('key') is not None and not isinstance(to_stix_config_prop.get('key'), dict):
255
+ # data variable is the final value, process in bulk
256
+ self._handle_value(data, parent_data, ds_sub_key, to_stix_config_prop, objects, object_tag_ref_map, object_key_ind)
257
+
258
+ elif isinstance(data, list):
259
+ for i, d in enumerate(data):
260
+ if isinstance(d, list) or isinstance(d, dict):
261
+ # Added parent key indexes to inner objects in order to handle nested lists of
262
+ # dictionaries and lists. For Example, if a list of IP address is present inside
263
+ # a list of network objects, then without adding this code, only the IP address information
264
+ # in the first network object will be created and the rest are not created.
265
+ if object_key_ind:
266
+ i = str(object_key_ind) + '_' + str(i)
267
+ # Inorder to include 0th index, the integer field 'i' is converted to string.
268
+ self._handle_properties(to_stix_config_prop, d, objects, object_tag_ref_map, data, ds_sub_key, str(i))
269
+ else:
270
+ # data variable is the final value, process in bulk
271
+ self._handle_value(data, parent_data, ds_sub_key, to_stix_config_prop, objects, object_tag_ref_map, object_key_ind)
272
+ break
273
+ # group the references of list of dictionary field
274
+ if isinstance(to_stix_config_prop, dict):
275
+ group_refs = [key for key, value in to_stix_config_prop.items() if
276
+ isinstance(value, dict) and value.get('group_ref') and value.get(
277
+ 'references')]
278
+ for group_ref in group_refs:
279
+ # Added a new boolean (True) parameter (is_group_ref) to indicate grouping of references
280
+ # through group_ref key in mapping
281
+ self._handle_value(data, to_stix_config_prop, ds_sub_key,
282
+ to_stix_config_prop[group_ref],
283
+ objects, object_tag_ref_map, object_key_ind, True)
284
+
285
+ elif isinstance(data, dict):
286
+ for k in data:
287
+ cust_prop = None
288
+ if k in to_stix_config_prop:
289
+ cust_prop = to_stix_config_prop[k]
290
+ elif self.options.get('unmapped_fallback') and k not in object_tag_ref_map['ds_key_cybox']:
291
+ cust_prop = {"key": "x-" + self.data_source.replace("_", "-") + "." + k, "object": "cust_object"}
292
+
293
+ if cust_prop:
294
+ self._handle_properties(cust_prop, data[k], objects, object_tag_ref_map, data, k, object_key_ind)
295
+ else:
296
+ self._handle_value(data, parent_data, ds_sub_key, to_stix_config_prop, objects, object_tag_ref_map, object_key_ind)
297
+ except Exception as e:
298
+ raise Exception("Error in json_to_stix_translator._handle_properties: %s" % e)
299
+
300
+
301
+ def _handle_value(self, data, parent_data, ds_sub_key, to_stix_config_prop, objects, object_tag_ref_map, object_key_ind=None, is_group_ref=False):
302
+ """
303
+ Receives the raw value of a data property, converts to a STIX valid value and adds to the cached observable `objects` dictionary
304
+ """
305
+ try:
306
+ if isinstance(to_stix_config_prop, dict):
307
+ props = [to_stix_config_prop]
308
+ else:
309
+ props = to_stix_config_prop
310
+
311
+ for prop in props:
312
+ key = prop.get('key', None)
313
+ if key is None:
314
+ continue
315
+
316
+ transformer = self.transformers[prop['transformer']] if 'transformer' in prop else None
317
+ references = references = prop['references'] if 'references' in prop else None
318
+
319
+ # This check avoid using duplicate reference in the multiple objects of the same type.
320
+ # For example: If there are multiple source ipv4-addr and network-traffic objects then
321
+ # without this reference check the first source ipv4-addr object will be referenced to all network-traffic objects.
322
+ if references:
323
+ if isinstance(references, str):
324
+ if object_key_ind:
325
+ references = references + '_' + str(object_key_ind)
326
+ elif not isinstance(data, list):
327
+ references = references + '_' + '0'
328
+ elif isinstance(references, list):
329
+ if object_key_ind:
330
+ references = [ref + '_' + str(object_key_ind) for ref in references]
331
+ elif not isinstance(data, list):
332
+ references = [ref + '_' + '0' for ref in references]
333
+ # unwrap array of stix values to separate stix objects
334
+ unwrap = True if 'unwrap' in prop and isinstance(data, list) else False
335
+ if "." in key:
336
+ cybox = True
337
+ else:
338
+ cybox = False
339
+
340
+ # cybox = prop.get('cybox', self.cybox_default)
341
+
342
+ if self.callback:
343
+ try:
344
+ generic_hash_key = self.callback(parent_data, ds_sub_key, key, self.options)
345
+ if generic_hash_key:
346
+ key = generic_hash_key
347
+ except Exception as e:
348
+ continue
349
+
350
+ config_keys = key.split('.')
351
+ if len(config_keys) < 2:
352
+ # if False is prop.get('cybox', self.cybox_default):
353
+ if not cybox:
354
+ object_tag_ref_map['out_cybox'][key] = self._compose_value_object(data, [], observable_key=key, object_tag_ref_map=object_tag_ref_map, transformer=transformer, references=references, unwrap=unwrap)
355
+ pass
356
+ else:
357
+ type_name = config_keys[0]
358
+ property_key = config_keys[1]
359
+ # set the object to combine properties from same SCO
360
+ parent_key = prop['object'] if 'object' in prop else type_name
361
+
362
+ # set the group to combine properties in a list
363
+ group = prop['group'] if 'group' in prop else False
364
+ substitute_key = prop['ds_key'] if 'ds_key' in prop else None
365
+
366
+ if False is cybox and not substitute_key:
367
+ value = self._compose_value_object(data, config_keys[2:], observable_key=key, object_tag_ref_map=object_tag_ref_map, transformer=transformer, references=references, unwrap=unwrap)
368
+ self._add_property(type_name, property_key, type_name, value, object_tag_ref_map['out_cybox'], cybox=False)
369
+ continue
370
+
371
+ if object_key_ind:
372
+ parent_key = parent_key + '_' + str(object_key_ind)
373
+ # Adding _0 as a tag index for the following
374
+ # 1. For object name of non-list type data or unwrapped data
375
+ # 2. For the object name containing references to list type of data.
376
+ # For Example, when a single IP address value and references to the list of domain values, needs
377
+ # to be added to a single custom object, the 0th index is added to the custom object to have ip
378
+ # address and references to list of domains under the same custom object.
379
+ elif (isinstance(data, list) and not unwrap and not references) \
380
+ or (isinstance(data, list) and references) or (not isinstance(data, list)):
381
+ parent_key = parent_key + '_' + '0'
382
+
383
+ # use the hard-coded value in the mapping
384
+ if 'value' in prop:
385
+ value = prop['value']
386
+ else:
387
+ if substitute_key and parent_data:
388
+ data = parent_data.get(substitute_key)
389
+
390
+ if False is cybox:
391
+ object_tag_ref_map['ds_key_cybox'][substitute_key] = True
392
+
393
+ value = self._compose_value_object(data, config_keys[2:], observable_key=key, object_tag_ref_map=object_tag_ref_map, transformer=transformer, references=references, unwrap=unwrap, is_group_ref=is_group_ref)
394
+
395
+ # Remove the values which has empty list brackets.
396
+ remove_value = False
397
+ if value is None or value in ('', []):
398
+ remove_value = True
399
+ elif isinstance(value, dict):
400
+ for k, v in value.items():
401
+ if isinstance(v, dict) and list(v.values())[0] in ('', []):
402
+ remove_value = True
403
+ continue
404
+ if remove_value:
405
+ continue
406
+
407
+ if not references and unwrap and isinstance(value, list):
408
+ for i, val_el in enumerate(value):
409
+ # Inorder to include 0th index, the integer field 'i' is converted to string.
410
+ parent_key_ind = self._get_tag_ind(parent_key, object_tag_ref_map, create_on_absence=True, unwrap=str(i), property_key=property_key)
411
+ self._add_property(type_name, property_key, parent_key_ind, val_el, objects, group=group)
412
+ else:
413
+ parent_key_ind = self._get_tag_ind(parent_key, object_tag_ref_map, create_on_absence=True, property_key=property_key)
414
+ self._add_property(type_name, property_key, parent_key_ind, value, objects, group=group)
415
+ except Exception as e:
416
+ raise Exception("Error in json_to_stix_translator._handle_value: %s : %s" % (e, e.__traceback__.tb_lineno))
417
+
418
+
419
+ def _generate_deterministic_id(self, cybox):
420
+ # Generates ID based on common namespace and SCO properties (omitting id and spec_version)
421
+
422
+ unique_id = None
423
+ cybox_properties = {}
424
+ cybox_type = cybox.get("type")
425
+ contributing_properties = self.contributing_properties_definitions.get(cybox_type)
426
+
427
+ if contributing_properties:
428
+ for contr_prop in contributing_properties:
429
+ if type(contr_prop) is list: # list of hash types
430
+ for hashtype in contr_prop:
431
+ hash_prop = "hashes.{}".format(hashtype)
432
+ if hash_prop in cybox:
433
+ cybox_properties[hash_prop] = cybox[hash_prop]
434
+ break
435
+ elif contr_prop in cybox and '_ref' not in contr_prop:
436
+ cybox_properties[contr_prop] = cybox[contr_prop]
437
+
438
+ if cybox_properties:
439
+ unique_id = cybox_type + "--" + str(uuid.uuid5(namespace=uuid.UUID(UUID5_NAMESPACE), name=json.dumps(cybox_properties, sort_keys=True, ensure_ascii=False, separators=(",", ":"))))
440
+
441
+ if not unique_id: # STIX process or custom object used UUID4 for identifier
442
+ unique_id = "{}--{}".format(cybox_type, str(uuid.uuid4()))
443
+
444
+ return unique_id
445
+
446
+
447
+ def transform(self, obj):
448
+ try:
449
+ """
450
+ Transforms the given object in to a STIX observation based on the mapping file and transform functions
451
+ :param obj: the datasource object that is being converted to stix
452
+ :return: the input object converted to stix valid json
453
+ """
454
+ object_map = {}
455
+ stix_type = 'observed-data'
456
+ ds_map = self.ds_to_stix_map
457
+ now = "{}Z".format(datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3])
458
+ object_id_map = {}
459
+
460
+ observation = {
461
+ 'id': stix_type + '--' + str(uuid.uuid4()),
462
+ 'type': stix_type,
463
+ 'created_by_ref': self.identity_id,
464
+ 'created': now,
465
+ 'modified': now,
466
+ 'objects': {}
467
+ }
468
+
469
+ # create normal type objects
470
+ if isinstance(obj, dict):
471
+ object_tag_ref_map = {UUID5_NAMESPACE: 0, 'tags': {}, 'non_ref_props': {}, 'out_cybox': {}, 'ds_key_cybox': {}}
472
+
473
+ self._handle_properties(ds_map, obj, object_map, object_tag_ref_map)
474
+ # special case:
475
+ # remove object if:
476
+ # a reference attribute object does not contain at least one property other than 'type'
477
+ object_map = self._cleanup_references(object_map, object_tag_ref_map['non_ref_props'])
478
+ observation['objects'] = object_map
479
+
480
+ for k, v in object_tag_ref_map['out_cybox'].items():
481
+ observation[k] = v
482
+
483
+ else:
484
+ self.logger.debug("Not a dict: {}".format(obj))
485
+
486
+ # Add required properties to the observation if it wasn't added from the mapping
487
+ if FIRST_OBSERVED_KEY not in observation:
488
+ observation[FIRST_OBSERVED_KEY] = now
489
+ if LAST_OBSERVED_KEY not in observation:
490
+ observation[LAST_OBSERVED_KEY] = now
491
+ if NUMBER_OBSERVED_KEY not in observation:
492
+ observation[NUMBER_OBSERVED_KEY] = 1
493
+
494
+ if self.spec_version == "2.1":
495
+ object_refs = []
496
+
497
+ for key, value in observation["objects"].items():
498
+ unique_id = self._generate_deterministic_id(value)
499
+ if unique_id:
500
+ if isinstance(value['id'], StixObjectId):
501
+ value['id'].update(unique_id)
502
+
503
+ if unique_id not in object_refs:
504
+ object_refs.append(unique_id)
505
+ self.unique_cybox_objects[unique_id] = value
506
+
507
+ observation["object_refs"] = object_refs
508
+ observation["spec_version"] = "2.1"
509
+
510
+ except Exception as e:
511
+ raise Exception("Error in json_to_stix_translator.transform %s : %s" % (e, e.__traceback__.tb_lineno))
512
+
513
+ return observation
514
+
515
+
516
+ def _cleanup_references(self, objects, tags):
517
+ new_objects = {}
518
+ tag_keys = list(tags.keys())
519
+
520
+ if not self.spec_version == "2.1":
521
+ try:
522
+ tag_keys.sort(key=lambda x: int(x))
523
+ except Exception:
524
+ pass
525
+
526
+ for ind in tag_keys:
527
+ new_objects[ind] = objects[ind]
528
+
529
+ return new_objects