pycti 6.0.10__py3-none-any.whl → 6.1.1__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.

Potentially problematic release.


This version of pycti might be problematic. Click here for more details.

pycti/__init__.py CHANGED
@@ -1,5 +1,5 @@
1
1
  # -*- coding: utf-8 -*-
2
- __version__ = "6.0.10"
2
+ __version__ = "6.1.1"
3
3
 
4
4
  from .api.opencti_api_client import OpenCTIApiClient
5
5
  from .api.opencti_api_connector import OpenCTIApiConnector
@@ -207,6 +207,12 @@ class OpenCTIApiClient:
207
207
  def set_applicant_id_header(self, applicant_id):
208
208
  self.request_headers["opencti-applicant-id"] = applicant_id
209
209
 
210
+ def set_playbook_id_header(self, playbook_id):
211
+ self.request_headers["opencti-playbook-id"] = playbook_id
212
+
213
+ def set_event_id(self, event_id):
214
+ self.request_headers["opencti-event-id"] = event_id
215
+
210
216
  def set_synchronized_upsert_header(self, synchronized):
211
217
  self.request_headers["synchronized-upsert"] = (
212
218
  "true" if synchronized is True else "false"
@@ -616,18 +622,19 @@ class OpenCTIApiClient:
616
622
  """upload a file to OpenCTI API
617
623
 
618
624
  :param `**kwargs`: arguments for file upload (required: `file_name` and `data`)
619
- :return: returns the query respons for the file upload
625
+ :return: returns the query response for the file upload
620
626
  :rtype: dict
621
627
  """
622
628
 
623
629
  file_name = kwargs.get("file_name", None)
630
+ file_markings = kwargs.get("file_markings", None)
624
631
  data = kwargs.get("data", None)
625
632
  mime_type = kwargs.get("mime_type", "text/plain")
626
633
  if file_name is not None:
627
634
  self.app_logger.info("Uploading a file.")
628
635
  query = """
629
- mutation UploadImport($file: Upload!) {
630
- uploadImport(file: $file) {
636
+ mutation UploadImport($file: Upload!, $fileMarkings: [String]) {
637
+ uploadImport(file: $file, fileMarkings: $fileMarkings) {
631
638
  id
632
639
  name
633
640
  }
@@ -639,8 +646,11 @@ class OpenCTIApiClient:
639
646
  mime_type = "application/json"
640
647
  else:
641
648
  mime_type = magic.from_file(file_name, mime=True)
642
-
643
- return self.query(query, {"file": (File(file_name, data, mime_type))})
649
+ query_vars = {"file": (File(file_name, data, mime_type))}
650
+ # optional file markings
651
+ if file_markings is not None:
652
+ query_vars["fileMarkings"] = file_markings
653
+ return self.query(query, query_vars)
644
654
  else:
645
655
  self.app_logger.error("[upload] Missing parameter: file_name")
646
656
  return None
@@ -9,14 +9,15 @@ class OpenCTIApiPlaybook:
9
9
  "Executing playbook step", {"playbook_id": playbook["playbook_id"]}
10
10
  )
11
11
  query = """
12
- mutation PlaybookStepExecution($execution_id: ID!, $execution_start: DateTime!, $data_instance_id: ID!, $playbook_id: ID!, $previous_step_id: ID!, $step_id: ID!, $previous_bundle: String!, $bundle: String!) {
13
- playbookStepExecution(execution_id: $execution_id, execution_start: $execution_start, data_instance_id: $data_instance_id, playbook_id: $playbook_id, previous_step_id: $previous_step_id, step_id: $step_id, previous_bundle: $previous_bundle, bundle: $bundle)
12
+ mutation PlaybookStepExecution($execution_id: ID!, $event_id: ID!, $execution_start: DateTime!, $data_instance_id: ID!, $playbook_id: ID!, $previous_step_id: ID!, $step_id: ID!, $previous_bundle: String!, $bundle: String!) {
13
+ playbookStepExecution(execution_id: $execution_id, event_id: $event_id, execution_start: $execution_start, data_instance_id: $data_instance_id, playbook_id: $playbook_id, previous_step_id: $previous_step_id, step_id: $step_id, previous_bundle: $previous_bundle, bundle: $bundle)
14
14
  }
15
15
  """
16
16
  self.api.query(
17
17
  query,
18
18
  {
19
19
  "execution_id": playbook["execution_id"],
20
+ "event_id": playbook["event_id"],
20
21
  "execution_start": playbook["execution_start"],
21
22
  "playbook_id": playbook["playbook_id"],
22
23
  "data_instance_id": playbook["data_instance_id"],
@@ -282,18 +282,20 @@ class ListenQueue(threading.Thread):
282
282
  is_playbook = "playbook" in json_data["internal"]
283
283
  # If playbook, compute object on data bundle
284
284
  if is_playbook:
285
- execution_id = json_data["internal"]["playbook"]["execution_id"]
286
285
  execution_start = self.helper.date_now()
287
- playbook_id = json_data["internal"]["playbook"]["playbook_id"]
288
- data_instance_id = json_data["internal"]["playbook"][
286
+ event_id = json_data["internal"]["playbook"].get("event_id")
287
+ execution_id = json_data["internal"]["playbook"].get("execution_id")
288
+ playbook_id = json_data["internal"]["playbook"].get("playbook_id")
289
+ data_instance_id = json_data["internal"]["playbook"].get(
289
290
  "data_instance_id"
290
- ]
291
+ )
291
292
  previous_bundle = json.dumps((json_data["event"]["bundle"]))
292
293
  step_id = json_data["internal"]["playbook"]["step_id"]
293
294
  previous_step_id = json_data["internal"]["playbook"][
294
295
  "previous_step_id"
295
296
  ]
296
297
  playbook_data = {
298
+ "event_id": event_id,
297
299
  "execution_id": execution_id,
298
300
  "execution_start": execution_start,
299
301
  "playbook_id": playbook_id,
@@ -312,7 +314,9 @@ class ListenQueue(threading.Thread):
312
314
  # If not playbook but enrichment, compute object on enrichment_entity
313
315
  opencti_entity = event_data["enrichment_entity"]
314
316
  stix_objects = self.helper.api.stix2.prepare_export(
315
- self.helper.api.stix2.generate_export(copy.copy(opencti_entity))
317
+ entity=self.helper.api.stix2.generate_export(
318
+ copy.copy(opencti_entity)
319
+ )
316
320
  )
317
321
  stix_entity = [
318
322
  e
@@ -1565,7 +1569,7 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
1565
1569
  if bundle is None:
1566
1570
  # Generate bundle
1567
1571
  stix_objects = self.api.stix2.prepare_export(
1568
- self.api.stix2.generate_export(copy.copy(opencti_entity))
1572
+ entity=self.api.stix2.generate_export(copy.copy(opencti_entity))
1569
1573
  )
1570
1574
  else:
1571
1575
  stix_objects = bundle["objects"]
@@ -530,7 +530,7 @@ class CaseIncident:
530
530
  data = self.opencti.process_multiple(result["data"]["caseIncidents"])
531
531
  final_data = final_data + data
532
532
  while result["data"]["caseIncidents"]["pageInfo"]["hasNextPage"]:
533
- after = result["date"]["caseIncidents"]["pageInfo"]["endCursor"]
533
+ after = result["data"]["caseIncidents"]["pageInfo"]["endCursor"]
534
534
  self.opencti.app_logger.info("Listing Case Incidents", {"after": after})
535
535
  result = self.opencti.query(
536
536
  query,
@@ -275,20 +275,40 @@ class IntrusionSet:
275
275
  }
276
276
  """
277
277
  )
278
- result = self.opencti.query(
279
- query,
280
- {
281
- "filters": filters,
282
- "search": search,
283
- "first": first,
284
- "after": after,
285
- "orderBy": order_by,
286
- "orderMode": order_mode,
287
- },
288
- )
289
- return self.opencti.process_multiple(
290
- result["data"]["intrusionSets"], with_pagination
291
- )
278
+ variables = {
279
+ "filters": filters,
280
+ "search": search,
281
+ "first": first,
282
+ "after": after,
283
+ "orderBy": order_by,
284
+ "orderMode": order_mode,
285
+ }
286
+ result = self.opencti.query(query, variables)
287
+ if get_all:
288
+ final_data = []
289
+ data = self.opencti.process_multiple(result["data"]["intrusionSets"])
290
+ final_data = final_data + data
291
+ while result["data"]["intrusionSets"]["pageInfo"]["hasNextPage"]:
292
+ after = result["data"]["intrusionSets"]["pageInfo"]["endCursor"]
293
+ self.opencti.app_logger.info("Listing Intrusion-Sets", {"after": after})
294
+ result = self.opencti.query(
295
+ query,
296
+ {
297
+ "filters": filters,
298
+ "search": search,
299
+ "first": first,
300
+ "after": after,
301
+ "orderBy": order_by,
302
+ "orderMode": order_mode,
303
+ },
304
+ )
305
+ data = self.opencti.process_multiple(result["data"]["intrusionSets"])
306
+ final_data = final_data + data
307
+ return final_data
308
+ else:
309
+ return self.opencti.process_multiple(
310
+ result["data"]["intrusionSets"], with_pagination
311
+ )
292
312
 
293
313
  """
294
314
  Read a Intrusion-Set object
@@ -466,6 +466,19 @@ class Report:
466
466
  id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
467
467
  return "report--" + id
468
468
 
469
+ @staticmethod
470
+ def generate_fixed_fake_id(name, published=None):
471
+ name = name.lower().strip()
472
+ if isinstance(published, datetime.datetime):
473
+ published = published.isoformat()
474
+ if published is not None:
475
+ data = {"name": name, "published": published, "fake": "fake"}
476
+ else:
477
+ data = {"name": name, "fake": "fake"}
478
+ data = canonicalize(data, utf8=False)
479
+ id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
480
+ return "report--" + id
481
+
469
482
  """
470
483
  List Report objects
471
484
 
@@ -435,9 +435,18 @@ class StixCoreObject:
435
435
  }
436
436
  }
437
437
  }
438
- }
438
+ }
439
439
  ... on StixCyberObservable {
440
440
  observable_value
441
+ indicators {
442
+ edges {
443
+ node {
444
+ id
445
+ pattern
446
+ pattern_type
447
+ }
448
+ }
449
+ }
441
450
  }
442
451
  ... on AutonomousSystem {
443
452
  number
@@ -1090,9 +1099,18 @@ class StixCoreObject:
1090
1099
  }
1091
1100
  }
1092
1101
  }
1093
- }
1102
+ }
1094
1103
  ... on StixCyberObservable {
1095
1104
  observable_value
1105
+ indicators {
1106
+ edges {
1107
+ node {
1108
+ id
1109
+ pattern
1110
+ pattern_type
1111
+ }
1112
+ }
1113
+ }
1096
1114
  }
1097
1115
  ... on AutonomousSystem {
1098
1116
  number
@@ -1459,13 +1477,21 @@ class StixCoreObject:
1459
1477
  return entity["importFiles"]
1460
1478
 
1461
1479
  def push_list_export(
1462
- self, entity_id, entity_type, file_name, data, list_filters="", mime_type=None
1480
+ self,
1481
+ entity_id,
1482
+ entity_type,
1483
+ file_name,
1484
+ file_markings,
1485
+ data,
1486
+ list_filters="",
1487
+ mime_type=None,
1463
1488
  ):
1464
1489
  query = """
1465
- mutation StixCoreObjectsExportPush($entity_id: String, $entity_type: String!, $file: Upload!, $listFilters: String) {
1466
- stixCoreObjectsExportPush(entity_id: $entity_id, entity_type: $entity_type, file: $file, listFilters: $listFilters)
1490
+ mutation StixCoreObjectsExportPush($entity_id: String, $entity_type: String!, $file: Upload!, $file_markings: [String]!, $listFilters: String) {
1491
+ stixCoreObjectsExportPush(entity_id: $entity_id, entity_type: $entity_type, file: $file, file_markings: $file_markings, listFilters: $listFilters)
1467
1492
  }
1468
1493
  """
1494
+
1469
1495
  if mime_type is None:
1470
1496
  file = self.file(file_name, data)
1471
1497
  else:
@@ -1476,6 +1502,7 @@ class StixCoreObject:
1476
1502
  "entity_id": entity_id,
1477
1503
  "entity_type": entity_type,
1478
1504
  "file": file,
1505
+ "file_markings": file_markings,
1479
1506
  "listFilters": list_filters,
1480
1507
  },
1481
1508
  )
@@ -2316,11 +2316,30 @@ class StixCyberObservable:
2316
2316
  return False
2317
2317
 
2318
2318
  def push_list_export(
2319
- self, entity_id, entity_type, file_name, data, list_filters="", mime_type=None
2319
+ self,
2320
+ entity_id,
2321
+ entity_type,
2322
+ file_name,
2323
+ file_markings,
2324
+ data,
2325
+ list_filters="",
2326
+ mime_type=None,
2320
2327
  ):
2321
2328
  query = """
2322
- mutation StixCyberObservablesExportPush($entity_id: String, $entity_type: String!, $file: Upload!, $listFilters: String) {
2323
- stixCyberObservablesExportPush(entity_id: $entity_id, entity_type: $entity_type, file: $file, listFilters: $listFilters)
2329
+ mutation StixCyberObservablesExportPush(
2330
+ $entity_id: String,
2331
+ $entity_type: String!,
2332
+ $file: Upload!,
2333
+ $file_markings: [String]!,
2334
+ $listFilters: String
2335
+ ) {
2336
+ stixCyberObservablesExportPush(
2337
+ entity_id: $entity_id,
2338
+ entity_type: $entity_type,
2339
+ file: $file,
2340
+ file_markings: $file_markings,
2341
+ listFilters: $listFilters
2342
+ )
2324
2343
  }
2325
2344
  """
2326
2345
  if mime_type is None:
@@ -2333,6 +2352,7 @@ class StixCyberObservable:
2333
2352
  "entity_id": entity_id,
2334
2353
  "entity_type": entity_type,
2335
2354
  "file": file,
2355
+ "file_markings": file_markings,
2336
2356
  "listFilters": list_filters,
2337
2357
  },
2338
2358
  )
@@ -1325,11 +1325,18 @@ class StixDomainObject:
1325
1325
  return None
1326
1326
 
1327
1327
  def push_list_export(
1328
- self, entity_id, entity_type, file_name, data, list_filters="", mime_type=None
1328
+ self,
1329
+ entity_id,
1330
+ entity_type,
1331
+ file_name,
1332
+ file_markings,
1333
+ data,
1334
+ list_filters="",
1335
+ mime_type=None,
1329
1336
  ):
1330
1337
  query = """
1331
- mutation StixDomainObjectsExportPush($entity_id: String, $entity_type: String!, $file: Upload!, $listFilters: String) {
1332
- stixDomainObjectsExportPush(entity_id: $entity_id, entity_type: $entity_type, file: $file, listFilters: $listFilters)
1338
+ mutation StixDomainObjectsExportPush($entity_id: String, $entity_type: String!, $file: Upload!, $file_markings: [String]!, $listFilters: String) {
1339
+ stixDomainObjectsExportPush(entity_id: $entity_id, entity_type: $entity_type, file: $file, file_markings: $file_markings, listFilters: $listFilters)
1333
1340
  }
1334
1341
  """
1335
1342
  if mime_type is None:
@@ -1342,15 +1349,26 @@ class StixDomainObject:
1342
1349
  "entity_id": entity_id,
1343
1350
  "entity_type": entity_type,
1344
1351
  "file": file,
1352
+ "file_markings": file_markings,
1345
1353
  "listFilters": list_filters,
1346
1354
  },
1347
1355
  )
1348
1356
 
1349
- def push_entity_export(self, entity_id, file_name, data, mime_type=None):
1357
+ def push_entity_export(
1358
+ self, entity_id, file_name, data, file_markings=None, mime_type=None
1359
+ ):
1360
+ if file_markings is None:
1361
+ file_markings = []
1350
1362
  query = """
1351
- mutation StixDomainObjectEdit($id: ID!, $file: Upload!) {
1363
+ mutation StixDomainObjectEdit(
1364
+ $id: ID!, $file: Upload!,
1365
+ $file_markings: [String]!
1366
+ ) {
1352
1367
  stixDomainObjectEdit(id: $id) {
1353
- exportPush(file: $file)
1368
+ exportPush(
1369
+ file: $file,
1370
+ file_markings: $file_markings
1371
+ )
1354
1372
  }
1355
1373
  }
1356
1374
  """
@@ -1358,7 +1376,9 @@ class StixDomainObject:
1358
1376
  file = self.file(file_name, data)
1359
1377
  else:
1360
1378
  file = self.file(file_name, data, mime_type)
1361
- self.opencti.query(query, {"id": entity_id, "file": file})
1379
+ self.opencti.query(
1380
+ query, {"id": entity_id, "file": file, "file_markings": file_markings}
1381
+ )
1362
1382
 
1363
1383
  """
1364
1384
  Update the Identity author of a Stix-Domain-Object object (created_by)
@@ -1,3 +1,6 @@
1
+ import json
2
+
3
+
1
4
  class StixObjectOrStixRelationship:
2
5
  def __init__(self, opencti):
3
6
  self.opencti = opencti
@@ -329,11 +332,15 @@ class StixObjectOrStixRelationship:
329
332
  }
330
333
  ... on Case {
331
334
  name
332
- }
335
+ }
333
336
  ... on StixCyberObservable {
334
337
  observable_value
335
338
  }
336
339
  ... on StixCoreRelationship {
340
+ id
341
+ standard_id
342
+ entity_type
343
+ parent_types
337
344
  createdBy {
338
345
  ... on Identity {
339
346
  id
@@ -396,6 +403,10 @@ class StixObjectOrStixRelationship:
396
403
  stop_time
397
404
  }
398
405
  ... on StixSightingRelationship {
406
+ id
407
+ standard_id
408
+ entity_type
409
+ parent_types
399
410
  createdBy {
400
411
  ... on Identity {
401
412
  id
@@ -476,9 +487,9 @@ class StixObjectOrStixRelationship:
476
487
  )
477
488
  query = (
478
489
  """
479
- query StixObjectOrStixRelationship($id: String!) {
480
- stixObjectOrStixRelationship(id: $id) {
481
- """
490
+ query StixObjectOrStixRelationship($id: String!) {
491
+ stixObjectOrStixRelationship(id: $id) {
492
+ """
482
493
  + (
483
494
  custom_attributes
484
495
  if custom_attributes is not None
@@ -496,3 +507,76 @@ class StixObjectOrStixRelationship:
496
507
  else:
497
508
  self.opencti.app_logger.error("Missing parameters: id")
498
509
  return None
510
+
511
+ def list(self, **kwargs):
512
+ filters = kwargs.get("filters", None)
513
+ search = kwargs.get("search", None)
514
+ first = kwargs.get("first", 100)
515
+ after = kwargs.get("after", None)
516
+ get_all = kwargs.get("getAll", False)
517
+ with_pagination = kwargs.get("with_pagination", False)
518
+ custom_attributes = kwargs.get("customAttributes", None)
519
+
520
+ self.opencti.app_logger.info(
521
+ "Listing StixObjectOrStixRelationships with filters",
522
+ {"filters": json.dumps(filters)},
523
+ )
524
+ query = (
525
+ """
526
+ query StixObjectOrStixRelationships($filters: FilterGroup, $search: String, $first: Int, $after: ID) {
527
+ stixObjectOrStixRelationships(filters: $filters, search: $search, first: $first, after: $after) {
528
+ edges {
529
+ node {
530
+ """
531
+ + (custom_attributes if custom_attributes is not None else self.properties)
532
+ + """
533
+ }
534
+ }
535
+ pageInfo {
536
+ startCursor
537
+ endCursor
538
+ hasNextPage
539
+ hasPreviousPage
540
+ globalCount
541
+ }
542
+ }
543
+ }
544
+ """
545
+ )
546
+ variables = {
547
+ "filters": filters,
548
+ "search": search,
549
+ "first": first,
550
+ "after": after,
551
+ }
552
+ result = self.opencti.query(
553
+ query,
554
+ variables,
555
+ )
556
+
557
+ if get_all:
558
+ final_data = []
559
+ data = self.opencti.process_multiple(
560
+ result["data"]["stixObjectOrStixRelationships"]
561
+ )
562
+ final_data = final_data + data
563
+ while result["data"]["stixObjectOrStixRelationships"]["pageInfo"][
564
+ "hasNextPage"
565
+ ]:
566
+ after = result["data"]["stixObjectOrStixRelationships"]["pageInfo"][
567
+ "endCursor"
568
+ ]
569
+ self.opencti.app_logger.info(
570
+ "Listing stixObjectOrStixRelationships", {"after": after}
571
+ )
572
+ after_variables = {**variables, **{"after": after}}
573
+ result = self.opencti.query(query, after_variables)
574
+ data = self.opencti.process_multiple(
575
+ result["data"]["stixObjectOrStixRelationships"]
576
+ )
577
+ final_data = final_data + data
578
+ return final_data
579
+ else:
580
+ return self.opencti.process_multiple(
581
+ result["data"]["stixObjectOrStixRelationships"], with_pagination
582
+ )
pycti/utils/constants.py CHANGED
@@ -129,7 +129,6 @@ class MultipleRefRelationship(Enum):
129
129
  CHILD = "child"
130
130
  BODY_MULTIPART = "body-multipart"
131
131
  VALUES = "values"
132
- LINKED = "x_opencti_linked-to"
133
132
  SERVICE_DDL = "service-dll"
134
133
  INSTALLED_SOFTWARE = "installed-software"
135
134
  RELATION_ANALYSIS_SCO = "analysis-sco"
@@ -135,43 +135,6 @@ class OpenCTIStix2:
135
135
  return stix_object["aliases"]
136
136
  return None
137
137
 
138
- def check_max_marking_definition(
139
- self, max_marking_definition_entity: Dict, entity_marking_definitions: List
140
- ) -> bool:
141
- """checks if a list of marking definitions conforms with a given max level
142
-
143
- :param max_marking_definition_entity: the maximum allowed marking definition level
144
- :type max_marking_definition_entity: str, optional
145
- :param entity_marking_definitions: list of entities to check
146
- :type entity_marking_definitions: list
147
- :return: `True` if the list conforms with max marking definition
148
- :rtype: bool
149
- """
150
-
151
- # Max is not set, return True
152
- if max_marking_definition_entity is None:
153
- return True
154
- # Filter entity markings definition to the max_marking_definition type
155
- typed_entity_marking_definitions = []
156
- for entity_marking_definition in entity_marking_definitions:
157
- if (
158
- entity_marking_definition["definition_type"]
159
- == max_marking_definition_entity["definition_type"]
160
- ):
161
- typed_entity_marking_definitions.append(entity_marking_definition)
162
- # No entity marking defintions of the max_marking_definition type
163
- if len(typed_entity_marking_definitions) == 0:
164
- return True
165
-
166
- # Check if level is less or equal to max
167
- for typed_entity_marking_definition in typed_entity_marking_definitions:
168
- if (
169
- typed_entity_marking_definition["x_opencti_order"]
170
- <= max_marking_definition_entity["x_opencti_order"]
171
- ):
172
- return True
173
- return False
174
-
175
138
  def import_bundle_from_file(
176
139
  self, file_path: str, update: bool = False, types: List = None
177
140
  ) -> Optional[List]:
@@ -680,6 +643,9 @@ class OpenCTIStix2:
680
643
 
681
644
  author = self.resolve_author(title)
682
645
  report = self.opencti.report.create(
646
+ id=self.opencti.report.generate_fixed_fake_id(
647
+ title, published
648
+ ),
683
649
  name=title,
684
650
  createdBy=author["id"] if author is not None else None,
685
651
  objectMarking=[object_marking_ref_result["id"]],
@@ -788,6 +754,48 @@ class OpenCTIStix2:
788
754
  "reports": reports,
789
755
  }
790
756
 
757
+ def get_listers(self):
758
+ return {
759
+ "Stix-Core-Object": self.opencti.stix_core_object.list,
760
+ "Stix-Domain-Object": self.opencti.stix_domain_object.list,
761
+ "Administrative-Area": self.opencti.location.list,
762
+ "Attack-Pattern": self.opencti.attack_pattern.list,
763
+ "Campaign": self.opencti.campaign.list,
764
+ "Channel": self.opencti.channel.list,
765
+ "Event": self.opencti.event.list,
766
+ "Note": self.opencti.note.list,
767
+ "Observed-Data": self.opencti.observed_data.list,
768
+ "Opinion": self.opencti.opinion.list,
769
+ "Report": self.opencti.report.list,
770
+ "Grouping": self.opencti.grouping.list,
771
+ "Case-Incident": self.opencti.case_incident.list,
772
+ "Feedback": self.opencti.feedback.list,
773
+ "Case-Rfi": self.opencti.case_rfi.list,
774
+ "Case-Rft": self.opencti.case_rft.list,
775
+ "Task": self.opencti.task.list,
776
+ "Course-Of-Action": self.opencti.course_of_action.list,
777
+ "Data-Component": self.opencti.data_component.list,
778
+ "Data-Source": self.opencti.data_source.list,
779
+ "Identity": self.opencti.identity.list,
780
+ "Indicator": self.opencti.indicator.list,
781
+ "Infrastructure": self.opencti.infrastructure.list,
782
+ "Intrusion-Set": self.opencti.intrusion_set.list,
783
+ "Location": self.opencti.location.list,
784
+ "Language": self.opencti.language.list,
785
+ "Malware": self.opencti.malware.list,
786
+ "Malware-Analysis": self.opencti.malware_analysis.list,
787
+ "Threat-Actor": self.opencti.threat_actor_group.list,
788
+ "Threat-Actor-Group": self.opencti.threat_actor_group.list,
789
+ "Threat-Actor-Individual": self.opencti.threat_actor_individual.list,
790
+ "Tool": self.opencti.tool.list,
791
+ "Narrative": self.opencti.narrative.list,
792
+ "Vulnerability": self.opencti.vulnerability.list,
793
+ "Incident": self.opencti.incident.list,
794
+ "Stix-Cyber-Observable": self.opencti.stix_cyber_observable.list,
795
+ "stix-sighting-relationship": self.opencti.stix_sighting_relationship.list,
796
+ "stix-core-relationship": self.opencti.stix_core_relationship.list,
797
+ }
798
+
791
799
  def get_readers(self):
792
800
  return {
793
801
  "Attack-Pattern": self.opencti.attack_pattern.read,
@@ -1512,7 +1520,7 @@ class OpenCTIStix2:
1512
1520
  if "tasks" in entity:
1513
1521
  del entity["tasks"]
1514
1522
 
1515
- if "status" in entity:
1523
+ if "status" in entity and entity["status"] is not None:
1516
1524
  entity["x_opencti_workflow_id"] = entity["status"].get("id")
1517
1525
  if "status" in entity:
1518
1526
  del entity["status"]
@@ -1614,35 +1622,72 @@ class OpenCTIStix2:
1614
1622
 
1615
1623
  return {k: v for k, v in entity.items() if self.opencti.not_empty(v)}
1616
1624
 
1625
+ @staticmethod
1626
+ def prepare_id_filters_export(
1627
+ id: Union[str, List[str]], access_filter: Dict = None
1628
+ ) -> Dict:
1629
+ if access_filter is not None:
1630
+ return {
1631
+ "mode": "and",
1632
+ "filterGroups": [
1633
+ {
1634
+ "mode": "or",
1635
+ "filters": [
1636
+ {
1637
+ "key": "ids",
1638
+ "values": id if isinstance(id, list) else [id],
1639
+ }
1640
+ ],
1641
+ "filterGroups": [],
1642
+ },
1643
+ access_filter,
1644
+ ],
1645
+ "filters": [],
1646
+ }
1647
+ else:
1648
+ return {
1649
+ "mode": "and",
1650
+ "filterGroups": [],
1651
+ "filters": [
1652
+ {
1653
+ "key": "ids",
1654
+ "mode": "or",
1655
+ "values": id if isinstance(id, list) else [id],
1656
+ }
1657
+ ],
1658
+ }
1659
+
1617
1660
  def prepare_export(
1618
1661
  self,
1619
1662
  entity: Dict,
1620
1663
  mode: str = "simple",
1621
- max_marking_definition_entity: Dict = None,
1664
+ access_filter: Dict = None,
1622
1665
  no_custom_attributes: bool = False,
1623
1666
  ) -> List:
1624
- if (
1625
- self.check_max_marking_definition(
1626
- max_marking_definition_entity,
1627
- entity["objectMarking"] if "objectMarking" in entity else [],
1628
- )
1629
- is False
1630
- ):
1631
- self.opencti.app_logger.info(
1632
- "Marking definitions are less than max definition, not exporting.",
1633
- {"type": entity["type"]},
1634
- )
1635
- return []
1636
1667
  result = []
1637
1668
  objects_to_get = []
1638
1669
  relations_to_get = []
1670
+
1671
+ # Container
1672
+ if "objects" in entity and len(entity["objects"]) > 0:
1673
+ object_ids = list(map(lambda e: e["standard_id"], entity["objects"]))
1674
+ export_query_filter = self.prepare_id_filters_export(
1675
+ id=object_ids, access_filter=access_filter
1676
+ )
1677
+ filtered_objects = (
1678
+ self.opencti.opencti_stix_object_or_stix_relationship.list(
1679
+ filters=export_query_filter, getAll=True
1680
+ )
1681
+ )
1682
+ entity["objects"] = filtered_objects
1683
+
1639
1684
  # CreatedByRef
1640
1685
  if (
1641
1686
  not no_custom_attributes
1642
1687
  and "createdBy" in entity
1643
1688
  and entity["createdBy"] is not None
1644
1689
  ):
1645
- created_by = self.generate_export(entity["createdBy"])
1690
+ created_by = self.generate_export(entity=entity["createdBy"])
1646
1691
  if entity["type"] in STIX_CYBER_OBSERVABLE_MAPPING:
1647
1692
  entity["x_opencti_created_by_ref"] = created_by["id"]
1648
1693
  else:
@@ -1747,7 +1792,7 @@ class OpenCTIStix2:
1747
1792
  and len(entity["objects"]) > 0
1748
1793
  ):
1749
1794
  entity["object_refs"] = []
1750
- objects_to_get = entity["objects"]
1795
+ objects_to_get = entity["objects"] # To do differently
1751
1796
  for entity_object in entity["objects"]:
1752
1797
  if (
1753
1798
  entity["type"] == "report"
@@ -1819,22 +1864,62 @@ class OpenCTIStix2:
1819
1864
  entity["type"] = "sighting"
1820
1865
  entity["count"] = entity["attribute_count"]
1821
1866
  del entity["attribute_count"]
1822
- entity["sighting_of_ref"] = entity["from"]["standard_id"]
1823
- objects_to_get.append(entity["from"])
1824
- entity["where_sighted_refs"] = [entity["to"]["standard_id"]]
1825
- objects_to_get.append(entity["to"])
1867
+ from_to_check = entity["from"]["id"]
1868
+ relationships_from_filter = self.prepare_id_filters_export(
1869
+ id=from_to_check, access_filter=access_filter
1870
+ )
1871
+ x = self.opencti.opencti_stix_object_or_stix_relationship.list(
1872
+ filters=relationships_from_filter
1873
+ )
1874
+ if len(x) > 0:
1875
+ entity["sighting_of_ref"] = entity["from"]["id"]
1876
+ # handle from and to separately like Stix Core Relationship and call 2 requests
1877
+ objects_to_get.append(
1878
+ entity["from"]
1879
+ ) # what happen with unauthorized objects ?
1880
+
1881
+ to_to_check = [entity["to"]["id"]]
1882
+ relationships_to_filter = self.prepare_id_filters_export(
1883
+ id=to_to_check, access_filter=access_filter
1884
+ )
1885
+ y = self.opencti.opencti_stix_object_or_stix_relationship.list(
1886
+ filters=relationships_to_filter
1887
+ )
1888
+ if len(y) > 0:
1889
+ entity["where_sighted_refs"] = [entity["to"]["id"]]
1890
+ objects_to_get.append(entity["to"])
1891
+
1826
1892
  del entity["from"]
1827
1893
  del entity["to"]
1828
1894
  # Stix Core Relationship
1829
1895
  if "from" in entity or "to" in entity:
1830
1896
  entity["type"] = "relationship"
1831
1897
  if "from" in entity:
1832
- entity["source_ref"] = entity["from"]["standard_id"]
1833
- objects_to_get.append(entity["from"])
1898
+ from_to_check = entity["from"]["id"]
1899
+ relationships_from_filter = self.prepare_id_filters_export(
1900
+ id=from_to_check, access_filter=access_filter
1901
+ )
1902
+ x = self.opencti.opencti_stix_object_or_stix_relationship.list(
1903
+ filters=relationships_from_filter
1904
+ )
1905
+ if len(x) > 0:
1906
+ entity["source_ref"] = entity["from"]["id"]
1907
+ # handle from and to separately like Stix Core Relationship and call 2 requests
1908
+ objects_to_get.append(
1909
+ entity["from"]
1910
+ ) # what happen with unauthorized objects ?
1834
1911
  del entity["from"]
1835
1912
  if "to" in entity:
1836
- entity["target_ref"] = entity["to"]["standard_id"]
1837
- objects_to_get.append(entity["to"])
1913
+ to_to_check = [entity["to"]["id"]]
1914
+ relationships_to_filter = self.prepare_id_filters_export(
1915
+ id=to_to_check, access_filter=access_filter
1916
+ )
1917
+ y = self.opencti.opencti_stix_object_or_stix_relationship.list(
1918
+ filters=relationships_to_filter
1919
+ )
1920
+ if len(y) > 0:
1921
+ entity["target_ref"] = entity["to"]["id"]
1922
+ objects_to_get.append(entity["to"])
1838
1923
  del entity["to"]
1839
1924
  # Stix Domain Object
1840
1925
  if "attribute_abstract" in entity:
@@ -1877,7 +1962,7 @@ class OpenCTIStix2:
1877
1962
 
1878
1963
  # StixRefRelationship
1879
1964
  stix_nested_ref_relationships = self.opencti.stix_nested_ref_relationship.list(
1880
- fromId=entity["x_opencti_id"]
1965
+ fromId=entity["x_opencti_id"], filters=access_filter
1881
1966
  )
1882
1967
  for stix_nested_ref_relationship in stix_nested_ref_relationships:
1883
1968
  if "standard_id" in stix_nested_ref_relationship["to"]:
@@ -1921,8 +2006,8 @@ class OpenCTIStix2:
1921
2006
  return result
1922
2007
  elif mode == "full":
1923
2008
  uuids = [entity["id"]]
1924
- for x in result:
1925
- uuids.append(x["id"])
2009
+ for y in result:
2010
+ uuids.append(y["id"])
1926
2011
  # Get extra refs
1927
2012
  for key in entity.keys():
1928
2013
  if key.endswith("_ref"):
@@ -1964,80 +2049,50 @@ class OpenCTIStix2:
1964
2049
  )
1965
2050
  # Get extra relations (from AND to)
1966
2051
  stix_core_relationships = self.opencti.stix_core_relationship.list(
1967
- fromOrToId=entity["x_opencti_id"], getAll=True
2052
+ fromOrToId=entity["x_opencti_id"], getAll=True, filters=access_filter
1968
2053
  )
1969
2054
  for stix_core_relationship in stix_core_relationships:
1970
- if self.check_max_marking_definition(
1971
- max_marking_definition_entity,
1972
- (
1973
- stix_core_relationship["objectMarking"]
1974
- if "objectMarking" in stix_core_relationship
1975
- else None
1976
- ),
1977
- ):
1978
- objects_to_get.append(
1979
- stix_core_relationship["to"]
1980
- if stix_core_relationship["to"]["id"] != entity["x_opencti_id"]
1981
- else stix_core_relationship["from"]
1982
- )
1983
- relation_object_data = self.prepare_export(
1984
- self.generate_export(stix_core_relationship),
1985
- "simple",
1986
- max_marking_definition_entity,
1987
- )
1988
- relation_object_bundle = self.filter_objects(
1989
- uuids, relation_object_data
1990
- )
1991
- uuids = uuids + [x["id"] for x in relation_object_bundle]
1992
- result = result + relation_object_bundle
1993
- else:
1994
- self.opencti.app_logger.info(
1995
- "Marking definitions are less than max definition, "
1996
- "not exporting the relation AND the target entity.",
1997
- {
1998
- "type": stix_core_relationship["entity_type"],
1999
- "id": stix_core_relationship["id"],
2000
- },
2055
+ objects_to_get.append(
2056
+ stix_core_relationship["to"]
2057
+ if stix_core_relationship["to"]["id"] != entity["x_opencti_id"]
2058
+ else stix_core_relationship["from"]
2059
+ )
2060
+ relation_object_data = (
2061
+ self.prepare_export( # ICI -> remove max marking ?
2062
+ entity=self.generate_export(stix_core_relationship),
2063
+ mode="simple",
2064
+ access_filter=access_filter,
2001
2065
  )
2066
+ )
2067
+ relation_object_bundle = self.filter_objects(
2068
+ uuids, relation_object_data
2069
+ )
2070
+ uuids = uuids + [x["id"] for x in relation_object_bundle]
2071
+ result = result + relation_object_bundle
2072
+
2002
2073
  # Get sighting
2003
2074
  stix_sighting_relationships = self.opencti.stix_sighting_relationship.list(
2004
- fromOrToId=entity["x_opencti_id"],
2005
- getAll=True,
2075
+ fromOrToId=entity["x_opencti_id"], getAll=True, filters=access_filter
2006
2076
  )
2007
2077
  for stix_sighting_relationship in stix_sighting_relationships:
2008
- if self.check_max_marking_definition(
2009
- max_marking_definition_entity,
2010
- (
2011
- stix_sighting_relationship["objectMarking"]
2012
- if "objectMarking" in stix_sighting_relationship
2013
- else None
2014
- ),
2015
- ):
2016
- objects_to_get.append(
2017
- stix_sighting_relationship["to"]
2018
- if stix_sighting_relationship["to"]["id"]
2019
- != entity["x_opencti_id"]
2020
- else stix_sighting_relationship["from"]
2021
- )
2022
- relation_object_data = self.prepare_export(
2023
- self.generate_export(stix_sighting_relationship),
2024
- "simple",
2025
- max_marking_definition_entity,
2026
- )
2027
- relation_object_bundle = self.filter_objects(
2028
- uuids, relation_object_data
2029
- )
2030
- uuids = uuids + [x["id"] for x in relation_object_bundle]
2031
- result = result + relation_object_bundle
2032
- else:
2033
- self.opencti.app_logger.info(
2034
- "Marking definitions are less than max definition, "
2035
- "not exporting the relation AND the target entity.",
2036
- {
2037
- "type": stix_sighting_relationship["entity_type"],
2038
- "id": stix_sighting_relationship["id"],
2039
- },
2078
+ objects_to_get.append(
2079
+ stix_sighting_relationship["to"]
2080
+ if stix_sighting_relationship["to"]["id"] != entity["x_opencti_id"]
2081
+ else stix_sighting_relationship["from"]
2082
+ )
2083
+ relation_object_data = (
2084
+ self.prepare_export( # ICI -> remove max marking ?
2085
+ entity=self.generate_export(stix_sighting_relationship),
2086
+ mode="simple",
2087
+ access_filter=access_filter,
2040
2088
  )
2089
+ )
2090
+ relation_object_bundle = self.filter_objects(
2091
+ uuids, relation_object_data
2092
+ )
2093
+ uuids = uuids + [x["id"] for x in relation_object_bundle]
2094
+ result = result + relation_object_bundle
2095
+
2041
2096
  if no_custom_attributes:
2042
2097
  del entity["x_opencti_id"]
2043
2098
  # Export
@@ -2065,12 +2120,16 @@ class OpenCTIStix2:
2065
2120
  {"type": entity_object["entity_type"]}
2066
2121
  ),
2067
2122
  )
2068
- entity_object_data = do_read(id=entity_object["id"])
2123
+
2124
+ query_filters = self.prepare_id_filters_export(
2125
+ entity_object["id"], access_filter
2126
+ )
2127
+ entity_object_data = do_read(filters=query_filters)
2069
2128
  if entity_object_data is not None:
2070
2129
  stix_entity_object = self.prepare_export(
2071
- self.generate_export(entity_object_data),
2072
- "simple",
2073
- max_marking_definition_entity,
2130
+ entity=self.generate_export(entity_object_data),
2131
+ mode="simple",
2132
+ access_filter=access_filter,
2074
2133
  )
2075
2134
  # Add to result
2076
2135
  entity_object_bundle = self.filter_objects(
@@ -2078,9 +2137,18 @@ class OpenCTIStix2:
2078
2137
  )
2079
2138
  uuids = uuids + [x["id"] for x in entity_object_bundle]
2080
2139
  result = result + entity_object_bundle
2081
- for relation_object in relations_to_get:
2140
+ for (
2141
+ relation_object
2142
+ ) in relations_to_get: # never appended after initialization
2143
+
2144
+ def find_relation_object_data(current_relation_object):
2145
+ return current_relation_object.id == relation_object["id"]
2146
+
2082
2147
  relation_object_data = self.prepare_export(
2083
- self.opencti.stix_core_relationship.read(id=relation_object["id"])
2148
+ entity=filter(
2149
+ find_relation_object_data,
2150
+ self.opencti.stix_core_relationship.list(filters=access_filter),
2151
+ )
2084
2152
  )
2085
2153
  relation_object_bundle = self.filter_objects(
2086
2154
  uuids, relation_object_data
@@ -2097,7 +2165,6 @@ class OpenCTIStix2:
2097
2165
  report_object_data = self.opencti.report.to_stix2(
2098
2166
  entity=report,
2099
2167
  mode="simple",
2100
- max_marking_definition_entity=max_marking_definition_entity,
2101
2168
  )
2102
2169
  report_object_bundle = self.filter_objects(
2103
2170
  uuids, report_object_data
@@ -2116,7 +2183,6 @@ class OpenCTIStix2:
2116
2183
  # note_object_data = self.opencti.note.to_stix2(
2117
2184
  # entity=note,
2118
2185
  # mode="simple",
2119
- # max_marking_definition_entity=max_marking_definition_entity,
2120
2186
  # )
2121
2187
  # note_object_bundle = self.filter_objects(
2122
2188
  # uuids, note_object_data
@@ -2145,20 +2211,15 @@ class OpenCTIStix2:
2145
2211
  else:
2146
2212
  return []
2147
2213
 
2148
- def export_entity(
2214
+ def get_stix_bundle_or_object_from_entity_id(
2149
2215
  self,
2150
2216
  entity_type: str,
2151
2217
  entity_id: str,
2152
2218
  mode: str = "simple",
2153
- max_marking_definition: Dict = None,
2219
+ access_filter: Dict = None,
2154
2220
  no_custom_attributes: bool = False,
2155
2221
  only_entity: bool = False,
2156
2222
  ) -> Dict:
2157
- max_marking_definition_entity = (
2158
- self.opencti.marking_definition.read(id=max_marking_definition)
2159
- if max_marking_definition is not None
2160
- else None
2161
- )
2162
2223
  bundle = {
2163
2224
  "type": "bundle",
2164
2225
  "id": "bundle--" + str(uuid.uuid4()),
@@ -2174,11 +2235,8 @@ class OpenCTIStix2:
2174
2235
  if LocationTypes.has_value(entity_type):
2175
2236
  entity_type = "Location"
2176
2237
 
2177
- # Reader
2178
- reader = self.get_readers()
2179
- if StixCyberObservableTypes.has_value(entity_type):
2180
- entity_type = "Stix-Cyber-Observable"
2181
- do_read = reader.get(
2238
+ readers = self.get_readers()
2239
+ do_read = readers.get(
2182
2240
  entity_type, lambda **kwargs: self.unknown_type({"type": entity_type})
2183
2241
  )
2184
2242
  entity = do_read(id=entity_id)
@@ -2189,10 +2247,10 @@ class OpenCTIStix2:
2189
2247
  return bundle
2190
2248
  entity_standard_id = entity["standard_id"]
2191
2249
  stix_objects = self.prepare_export(
2192
- self.generate_export(entity, no_custom_attributes),
2193
- mode,
2194
- max_marking_definition_entity,
2195
- no_custom_attributes,
2250
+ entity=self.generate_export(entity, no_custom_attributes),
2251
+ mode=mode,
2252
+ access_filter=access_filter,
2253
+ no_custom_attributes=no_custom_attributes,
2196
2254
  )
2197
2255
  if stix_objects is not None:
2198
2256
  bundle["objects"].extend(stix_objects)
@@ -2202,15 +2260,35 @@ class OpenCTIStix2:
2202
2260
  ]
2203
2261
  return bundle
2204
2262
 
2263
+ # Please use get_stix_bundle_or_object_from_entity_id instead
2264
+ @DeprecationWarning
2265
+ def export_entity(
2266
+ self,
2267
+ entity_type: str,
2268
+ entity_id: str,
2269
+ mode: str = "simple",
2270
+ access_filter: Dict = None,
2271
+ no_custom_attributes: bool = False,
2272
+ only_entity: bool = False,
2273
+ ) -> Dict:
2274
+ return self.get_stix_bundle_or_object_from_entity_id(
2275
+ entity_type=entity_type,
2276
+ entity_id=entity_id,
2277
+ mode=mode,
2278
+ access_filter=access_filter,
2279
+ no_custom_attributes=no_custom_attributes,
2280
+ only_entity=only_entity,
2281
+ )
2282
+
2205
2283
  def export_entities_list(
2206
2284
  self,
2207
2285
  entity_type: str,
2208
2286
  search: Dict = None,
2209
- filters: List = None,
2287
+ filters: Dict = None,
2210
2288
  orderBy: str = None,
2211
2289
  orderMode: str = None,
2212
2290
  getAll: bool = True,
2213
- ) -> Dict:
2291
+ ) -> [Dict]:
2214
2292
  if IdentityTypes.has_value(entity_type):
2215
2293
  entity_type = "Identity"
2216
2294
 
@@ -2279,26 +2357,31 @@ class OpenCTIStix2:
2279
2357
  self,
2280
2358
  entity_type: str,
2281
2359
  search: Dict = None,
2282
- filters: List = None,
2360
+ filters: Dict = None,
2283
2361
  order_by: str = None,
2284
2362
  order_mode: str = None,
2285
2363
  mode: str = "simple",
2286
- max_marking_definition: Dict = None,
2364
+ access_filter: Dict = None,
2287
2365
  ) -> Dict:
2288
- max_marking_definition_entity = (
2289
- self.opencti.marking_definition.read(id=max_marking_definition)
2290
- if max_marking_definition is not None
2291
- else None
2292
- )
2293
2366
  bundle = {
2294
2367
  "type": "bundle",
2295
2368
  "id": "bundle--" + str(uuid.uuid4()),
2296
2369
  "objects": [],
2297
2370
  }
2371
+ filter_groups = []
2372
+ if filters is not None:
2373
+ filter_groups.append(filters)
2374
+ if access_filter is not None:
2375
+ filter_groups.append(access_filter)
2376
+ export_query_filter = {
2377
+ "mode": "and",
2378
+ "filterGroups": filter_groups,
2379
+ "filters": [],
2380
+ }
2298
2381
  entities_list = self.export_entities_list(
2299
2382
  entity_type=entity_type,
2300
2383
  search=search,
2301
- filters=filters,
2384
+ filters=export_query_filter,
2302
2385
  orderBy=order_by,
2303
2386
  orderMode=order_mode,
2304
2387
  getAll=True,
@@ -2307,9 +2390,9 @@ class OpenCTIStix2:
2307
2390
  uuids = []
2308
2391
  for entity in entities_list:
2309
2392
  entity_bundle = self.prepare_export(
2310
- self.generate_export(entity),
2311
- mode,
2312
- max_marking_definition_entity,
2393
+ entity=self.generate_export(entity),
2394
+ mode=mode,
2395
+ access_filter=access_filter,
2313
2396
  )
2314
2397
  if entity_bundle is not None:
2315
2398
  entity_bundle_filtered = self.filter_objects(uuids, entity_bundle)
@@ -2320,33 +2403,32 @@ class OpenCTIStix2:
2320
2403
 
2321
2404
  def export_selected(
2322
2405
  self,
2323
- entities_list: [str],
2406
+ entities_list: [dict],
2324
2407
  mode: str = "simple",
2325
- max_marking_definition: Dict = None,
2408
+ access_filter: Dict = None,
2326
2409
  ) -> Dict:
2327
- max_marking_definition_entity = (
2328
- self.opencti.marking_definition.read(id=max_marking_definition)
2329
- if max_marking_definition is not None
2330
- else None
2331
- )
2410
+
2332
2411
  bundle = {
2333
2412
  "type": "bundle",
2334
2413
  "id": "bundle--" + str(uuid.uuid4()),
2335
2414
  "objects": [],
2336
2415
  }
2337
- if entities_list is not None:
2338
- uuids = []
2339
- for entity in entities_list:
2340
- entity_bundle = self.prepare_export(
2341
- self.generate_export(entity),
2342
- mode,
2343
- max_marking_definition_entity,
2344
- )
2345
- if entity_bundle is not None:
2346
- entity_bundle_filtered = self.filter_objects(uuids, entity_bundle)
2347
- for x in entity_bundle_filtered:
2348
- uuids.append(x["id"])
2349
- bundle["objects"] = bundle["objects"] + entity_bundle_filtered
2416
+
2417
+ uuids = []
2418
+ for entity in entities_list:
2419
+ entity_bundle = self.prepare_export(
2420
+ entity=self.generate_export(entity),
2421
+ mode=mode,
2422
+ access_filter=access_filter,
2423
+ )
2424
+ if entity_bundle is not None:
2425
+ entity_bundle_filtered = self.filter_objects(uuids, entity_bundle)
2426
+ for x in entity_bundle_filtered:
2427
+ uuids.append(x["id"])
2428
+ bundle["objects"] = (
2429
+ bundle["objects"] + entity_bundle_filtered
2430
+ ) # unsupported operand type(s) for +: 'dict' and 'list'
2431
+
2350
2432
  return bundle
2351
2433
 
2352
2434
  def import_bundle(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pycti
3
- Version: 6.0.10
3
+ Version: 6.1.1
4
4
  Summary: Python API client for OpenCTI.
5
5
  Home-page: https://github.com/OpenCTI-Platform/client-python
6
6
  Author: Filigran
@@ -42,7 +42,7 @@ Requires-Dist: pre-commit ~=3.7.0 ; extra == 'dev'
42
42
  Requires-Dist: pytest-cases ~=3.8.0 ; extra == 'dev'
43
43
  Requires-Dist: pytest-cov ~=5.0.0 ; extra == 'dev'
44
44
  Requires-Dist: pytest-randomly ~=3.15.0 ; extra == 'dev'
45
- Requires-Dist: pytest ~=8.1.1 ; extra == 'dev'
45
+ Requires-Dist: pytest ~=8.2.0 ; extra == 'dev'
46
46
  Requires-Dist: types-python-dateutil ~=2.9.0 ; extra == 'dev'
47
47
  Requires-Dist: wheel ~=0.43.0 ; extra == 'dev'
48
48
  Provides-Extra: doc
@@ -1,17 +1,17 @@
1
- pycti/__init__.py,sha256=Ya18bCudQaEK_R_aYZlT8maVpKNSKDVKY6QvcBR36yA,5036
1
+ pycti/__init__.py,sha256=ef2UbUNokMidzoypuzd5uAVwhY5q-cU73jsqRdYXVEA,5035
2
2
  pycti/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- pycti/api/opencti_api_client.py,sha256=pMOXPMF7qPo484FJqKckH1F-PHn8HWZP2XMe8NXeSSU,29072
3
+ pycti/api/opencti_api_client.py,sha256=fDeVJjExlsrTNYRf28LYhifz901JkdOZueCdvvko36Y,29575
4
4
  pycti/api/opencti_api_connector.py,sha256=fYF0Jy9KIMFNt1RC_A1rpWomVJ-oj5HiSsBem4W0J5U,3549
5
- pycti/api/opencti_api_playbook.py,sha256=Wcf-G__IHmR7LwtUFVUVx4Skg9e2mcb89n_HyfWC9YM,1383
5
+ pycti/api/opencti_api_playbook.py,sha256=OkqDawpnMYIHz5sD4djlJ_KgORkfvQ7YbJwttxE-ea8,1470
6
6
  pycti/api/opencti_api_work.py,sha256=JLfl7oy6Cq9IrYW_kUrqwzN46FoVzyIn1JJQKyK0h_w,7615
7
7
  pycti/connector/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  pycti/connector/opencti_connector.py,sha256=0vrZ8Y8ecbxegAP1YhpX6ybOZahYjjOkcId51D1oBi4,2449
9
- pycti/connector/opencti_connector_helper.py,sha256=m2KFgmku_EYXpud-pz03JeNGVftzlX_zAbmN3xWSAts,60399
9
+ pycti/connector/opencti_connector_helper.py,sha256=knJ4fWDTMouVcD_o00UfjHr_jVerrzudl9Aqhu1BigM,60606
10
10
  pycti/connector/opencti_metric_handler.py,sha256=4jXHeJflomtHjuQ_YU0b36TG7o26vOWbY_jvU8Ezobs,3725
11
11
  pycti/entities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  pycti/entities/opencti_attack_pattern.py,sha256=ycAR0cReJ1dd_edQPAL6qBrFvvTx10GJFMTezyK91cg,21471
13
13
  pycti/entities/opencti_campaign.py,sha256=YHEN7hdY-E-1uVNyaZHCv9QPLALvOY08ErOzUKcvr6c,17107
14
- pycti/entities/opencti_case_incident.py,sha256=aoV-uj9VSvAczQot5_wao_07JomllLHjO76_deWAhUg,33699
14
+ pycti/entities/opencti_case_incident.py,sha256=BEP22Itt7-ZxMBwMNMHO7sbS8XfNSfiy0Y7chuOgWJ4,33699
15
15
  pycti/entities/opencti_case_rfi.py,sha256=wboFfQQnE7qjnqnLsw2nxmGAjH1eYjVgayKKAk7a8pI,31876
16
16
  pycti/entities/opencti_case_rft.py,sha256=RIefvKibRZ_Qrk4uybEGxnwJ2SAquPXYm4-uJ8RUskg,32760
17
17
  pycti/entities/opencti_channel.py,sha256=cA8ltYJpB43rUSKvCPyLUJvtdb_RSJ4tuuUdi_EMF2Q,16608
@@ -26,7 +26,7 @@ pycti/entities/opencti_identity.py,sha256=YKTQugOSwyxSoMRWSdMWZkagidTbYQzi-Q1aEW
26
26
  pycti/entities/opencti_incident.py,sha256=KAaqn0mnlyIKJ2whtbK0Eg8AbfQfxKvppS9hyfn_04s,18553
27
27
  pycti/entities/opencti_indicator.py,sha256=Kcy1BQQ1-P5OcsFHSzqHHDk0KGM6MhjzPwJiPCRShO0,27581
28
28
  pycti/entities/opencti_infrastructure.py,sha256=hpzlSP8yQCSjYyOvX_oynBJq0YMp-Uq7PRhP_L6j9wk,19353
29
- pycti/entities/opencti_intrusion_set.py,sha256=VTRjoAI6xSGrVmSpPFzsQ5KQJwrD__QdqVog3AhLwQk,17985
29
+ pycti/entities/opencti_intrusion_set.py,sha256=MXubQhDZ6LE69z1Wj8agAZNOrGeKiXU4IZs_9B0Expg,18957
30
30
  pycti/entities/opencti_kill_chain_phase.py,sha256=a509rFeIchPRlO5rMHPVxrxfUuTbV4znh6e_NJHC66I,8062
31
31
  pycti/entities/opencti_label.py,sha256=6RZJPIa_dXf_YNNU4xXKghfBnpNjhU5YXOaSIcB4YrM,8800
32
32
  pycti/entities/opencti_language.py,sha256=eHB7qzf_l2Mno_Wy9kF0QUdcBktWgr4kRHhb9AxT0c0,16176
@@ -38,14 +38,14 @@ pycti/entities/opencti_narrative.py,sha256=qiuFQub04pvArYf5hHJEgZtneR7p-VQq5Yywi
38
38
  pycti/entities/opencti_note.py,sha256=tGxOzSJiiHYZNrCpiVnvwY_qiBYURQ0G5wgL9dSWmhE,29938
39
39
  pycti/entities/opencti_observed_data.py,sha256=1WFOJLxOh3k-3cWvnyIiEk0hF92tpk5ohvPHeR2zVG4,30550
40
40
  pycti/entities/opencti_opinion.py,sha256=SPcY8-0zRJCMle-eDLka-CFPyAqU3CnVVBtfVYhzyJE,21837
41
- pycti/entities/opencti_report.py,sha256=tSMSCsAGgZny94EjFtidEws7Uf0gBI-nPHwkqPg2Elc,33037
41
+ pycti/entities/opencti_report.py,sha256=zKoq3Kpo3afvFsw0QCBOaeVm9J_xRMBOZfJC7ZPRaRg,33580
42
42
  pycti/entities/opencti_stix.py,sha256=uMheSg8i1f2Ozx2Mk0iShWzHHjj6MMWDtV5nDjVxKEE,2275
43
- pycti/entities/opencti_stix_core_object.py,sha256=R6GEDfXCPYvK4qrh9XbZsUEscvDYP8yo4Rjcvqx0_W0,48868
43
+ pycti/entities/opencti_stix_core_object.py,sha256=3jABOB_-vm2CSB6LU3ylxpSj_oixRCcfU3T10n2_MFU,49559
44
44
  pycti/entities/opencti_stix_core_relationship.py,sha256=cD825areOn2quv06M28YGE7A3bTQ8_Pxx1QW6JyoQBs,42895
45
- pycti/entities/opencti_stix_cyber_observable.py,sha256=WOVefLN_M_nWTZ7GOGNmPJ3F6jmuIIYb0w07DuYKMVo,106395
46
- pycti/entities/opencti_stix_domain_object.py,sha256=04q7gQZ-hgpsjsyvXh6VOYmRJOPwOrdAPlzUmzlM5mc,77998
45
+ pycti/entities/opencti_stix_cyber_observable.py,sha256=EOJuXeSmFcm4oI2rPOqxZ8QZq_ej_CTkYgCTtUkZwsk,106785
46
+ pycti/entities/opencti_stix_domain_object.py,sha256=QI6uBbefNC_PQSwl0O5KpG4cWqa-15mIju8dwREzooU,78504
47
47
  pycti/entities/opencti_stix_nested_ref_relationship.py,sha256=2r1i7cUl-WWictlnC_MJrm9sTIt_yJe2uqTpQm-yo6o,12330
48
- pycti/entities/opencti_stix_object_or_stix_relationship.py,sha256=NOJdUU9cm_gMBne13EZPb8cfxOhTfgVVmv8A0yasQeI,14610
48
+ pycti/entities/opencti_stix_object_or_stix_relationship.py,sha256=x0LWqMqaqqIPgQnLgw7Q2qPoXK4fZix8-KsQnmZaIOw,17696
49
49
  pycti/entities/opencti_stix_sighting_relationship.py,sha256=AmX1LBS8cW5a_dlik_sx-nBDvUcqb193gs4m3pB9C5U,27584
50
50
  pycti/entities/opencti_task.py,sha256=y4Q2vC-eLccX-yna85GnnCFEKWFJAWQcflNbkr9BClo,24668
51
51
  pycti/entities/opencti_threat_actor.py,sha256=lRdPhXX_HsNSE5rTwkke_U5T_FAPGD22ow2-YeZdaYc,9950
@@ -55,14 +55,14 @@ pycti/entities/opencti_tool.py,sha256=RJrBywXI7TfWvgga6M9hH4YBl32dsvro2z42V_EgzG
55
55
  pycti/entities/opencti_vocabulary.py,sha256=6JfOByggvSxvkfIXk1b60T7fyWOhxZ6YFkGbSeV8F-4,5988
56
56
  pycti/entities/opencti_vulnerability.py,sha256=dzJ0fZB2XrkPwT-cANr6atzYOWXF5nk0al86X7o2ZTo,20094
57
57
  pycti/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
- pycti/utils/constants.py,sha256=Yj6fORQqlY7d7FTZC_lH-FS2-hvM_NvlwpCaPCy4_Gs,10636
58
+ pycti/utils/constants.py,sha256=Gj0fz94p0ApjCUCUqBQpFTfNslT021HS2w6M8azqXBY,10601
59
59
  pycti/utils/opencti_logger.py,sha256=0dvB75V0SuPFGxL539dAQrxTt1N5Acx0A3Ogwl5WMJ8,2199
60
- pycti/utils/opencti_stix2.py,sha256=Xo-YDH-6ov4JFBi1MutBITcdKPeQJdkL3qwlWAozV7s,112766
60
+ pycti/utils/opencti_stix2.py,sha256=DB48oDbAKyEwwowHU7_0_p9Y74hXRMaA0IkJ-eschYU,115786
61
61
  pycti/utils/opencti_stix2_splitter.py,sha256=Ht9Mp-W3gbwxIKEr7i_5NYpcDr3TA2gYdC4TzOz0G4c,4496
62
62
  pycti/utils/opencti_stix2_update.py,sha256=CnMyqkeVA0jgyxEcgqna8sABU4YPMjkEJ228GVurIn4,14658
63
63
  pycti/utils/opencti_stix2_utils.py,sha256=4r9qglN3AIN8JH1B9Ts2o20Qn3K203M4c5-lIPzRpZ4,4138
64
- pycti-6.0.10.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
65
- pycti-6.0.10.dist-info/METADATA,sha256=A16k5ptxplOTpIyAxc4gqQB0qA7e1m9xMYb5_NPOrdQ,5314
66
- pycti-6.0.10.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
67
- pycti-6.0.10.dist-info/top_level.txt,sha256=cqEpxitAhHP4VgSA6xmrak6Yk9MeBkwoMTB6k7d2ZnE,6
68
- pycti-6.0.10.dist-info/RECORD,,
64
+ pycti-6.1.1.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
65
+ pycti-6.1.1.dist-info/METADATA,sha256=HiieXOK5ea_MGzOWNessuVTMkCCMbk9H4mUz9YDfED4,5313
66
+ pycti-6.1.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
67
+ pycti-6.1.1.dist-info/top_level.txt,sha256=cqEpxitAhHP4VgSA6xmrak6Yk9MeBkwoMTB6k7d2ZnE,6
68
+ pycti-6.1.1.dist-info/RECORD,,
File without changes