Skip to content
This repository was archived by the owner on Dec 5, 2025. It is now read-only.

Commit b319cc7

Browse files
committed
[client] handle too large items
1 parent 9f2f789 commit b319cc7

File tree

5 files changed

+45
-20
lines changed

5 files changed

+45
-20
lines changed

pycti/utils/opencti_stix2.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import time
99
import traceback
1010
import uuid
11-
from typing import Any, Dict, List, Optional, Union
11+
from typing import Any, Dict, List, Optional, Union, Tuple
1212

1313
import datefinder
1414
import dateutil.parser
@@ -196,7 +196,7 @@ def import_bundle_from_file(
196196
file_path: str,
197197
update: bool = False,
198198
types: List = None,
199-
) -> Optional[List]:
199+
) -> Optional[Tuple[list, list]]:
200200
"""import a stix2 bundle from a file
201201
202202
:param file_path: valid path to the file
@@ -221,7 +221,8 @@ def import_bundle_from_json(
221221
update: bool = False,
222222
types: List = None,
223223
work_id: str = None,
224-
) -> List:
224+
objects_max_deps: int = 0,
225+
) -> Tuple[list, list]:
225226
"""import a stix2 bundle from JSON data
226227
227228
:param json_data: JSON data
@@ -231,11 +232,13 @@ def import_bundle_from_json(
231232
:param types: list of stix2 types, defaults to None
232233
:type types: list, optional
233234
:param work_id work_id: str, optional
234-
:return: list of imported stix2 objects
235-
:rtype: List
235+
:param objects_max_deps: max deps amount of objects, reject object import if larger than configured amount
236+
:type objects_max_deps: int, optional
237+
:return: list of imported stix2 objects and a list of stix2 objects with too many deps
238+
:rtype: Tuple[List,List]
236239
"""
237240
data = json.loads(json_data)
238-
return self.import_bundle(data, update, types, work_id)
241+
return self.import_bundle(data, update, types, work_id, objects_max_deps)
239242

240243
def resolve_author(self, title: str) -> Optional[Identity]:
241244
if "fireeye" in title.lower() or "mandiant" in title.lower():
@@ -3054,7 +3057,8 @@ def import_bundle(
30543057
update: bool = False,
30553058
types: List = None,
30563059
work_id: str = None,
3057-
) -> List:
3060+
objects_max_deps: int = 0,
3061+
) -> Tuple[list, list]:
30583062
# Check if the bundle is correctly formatted
30593063
if "type" not in stix_bundle or stix_bundle["type"] != "bundle":
30603064
raise ValueError("JSON data type is not a STIX2 bundle")
@@ -3066,8 +3070,8 @@ def import_bundle(
30663070
else None
30673071
)
30683072

3069-
stix2_splitter = OpenCTIStix2Splitter()
3070-
_, incompatible_elements, bundles = (
3073+
stix2_splitter = OpenCTIStix2Splitter(objects_max_deps)
3074+
_, incompatible_elements, bundles, too_large_elements_bundles = (
30713075
stix2_splitter.split_bundle_with_expectations(
30723076
stix_bundle, False, event_version
30733077
)
@@ -3093,7 +3097,7 @@ def import_bundle(
30933097
self.import_item(item, update, types, 0, work_id)
30943098
imported_elements.append({"id": item["id"], "type": item["type"]})
30953099

3096-
return imported_elements
3100+
return imported_elements, too_large_elements_bundles
30973101

30983102
@staticmethod
30993103
def put_attribute_in_extension(

pycti/utils/opencti_stix2_splitter.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,19 @@ def is_id_supported(key):
3333
return True
3434

3535

36-
class OpenCTIStix2Splitter:
36+
class OpenCTIStix2Splitter: # pylint: disable=too-many-instance-attributes
3737
"""STIX2 bundle splitter for OpenCTI
3838
3939
Splits large STIX2 bundles into smaller chunks for processing.
4040
"""
4141

42-
def __init__(self):
42+
def __init__(self, objects_max_deps: int = 0):
43+
self.objects_max_deps = objects_max_deps
4344
self.cache_index = {}
4445
self.cache_refs = {}
4546
self.elements = []
4647
self.incompatible_items = []
48+
self.too_large_elements = []
4749

4850
def get_internal_ids_in_extension(self, item):
4951
ids = []
@@ -196,7 +198,9 @@ def enlist_element(
196198
)
197199
else:
198200
is_compatible = is_id_supported(item_id)
199-
if is_compatible:
201+
if self.objects_max_deps is not 0 and nb_deps >= self.objects_max_deps:
202+
self.too_large_elements.append(item)
203+
elif is_compatible:
200204
self.elements.append(item)
201205
else:
202206
self.incompatible_items.append(item)
@@ -212,7 +216,7 @@ def split_bundle_with_expectations(
212216
use_json=True,
213217
event_version=None,
214218
cleanup_inconsistent_bundle=False,
215-
) -> Tuple[int, list, list]:
219+
) -> Tuple[int, list, list, list]:
216220
"""splits a valid stix2 bundle into a list of bundles"""
217221
if use_json:
218222
try:
@@ -262,11 +266,28 @@ def by_dep_size(elem):
262266
)
263267
)
264268

265-
return number_expectations, self.incompatible_items, bundles
269+
too_large_elements_bundles = []
270+
for too_large_element in self.too_large_elements:
271+
too_large_elements_bundles.append(
272+
self.stix2_create_bundle(
273+
bundle_data["id"],
274+
too_large_element["nb_deps"],
275+
[too_large_element],
276+
use_json,
277+
event_version,
278+
)
279+
)
280+
281+
return (
282+
number_expectations,
283+
self.incompatible_items,
284+
bundles,
285+
too_large_elements_bundles,
286+
)
266287

267288
@deprecated("Use split_bundle_with_expectations instead")
268289
def split_bundle(self, bundle, use_json=True, event_version=None) -> list:
269-
_, _, bundles = self.split_bundle_with_expectations(
290+
_, _, bundles, _ = self.split_bundle_with_expectations(
270291
bundle, use_json, event_version
271292
)
272293
return bundles

tests/01-unit/utils/test_opencti_stix2_splitter.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,11 @@ def test_split_internal_ids_bundle():
8080
stix_splitter = OpenCTIStix2Splitter()
8181
with open("./tests/data/bundle_with_internal_ids.json") as file:
8282
content = file.read()
83-
expectations, _, bundles = stix_splitter.split_bundle_with_expectations(content)
83+
expectations, _, bundles, _ = stix_splitter.split_bundle_with_expectations(content)
8484
assert expectations == 4
8585
# Split with cleanup_inconsistent_bundle
8686
stix_splitter = OpenCTIStix2Splitter()
87-
expectations, _, bundles = stix_splitter.split_bundle_with_expectations(
87+
expectations, _, bundles, _ = stix_splitter.split_bundle_with_expectations(
8888
bundle=content, cleanup_inconsistent_bundle=True
8989
)
9090
assert expectations == 4

tests/02-integration/entities/test_malware.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ def test_malware_import_with_sample_refs(api_client):
55
with open("tests/data/basicMalwareWithSample.json", "r") as content_file:
66
content = content_file.read()
77

8-
imported_malware_bundle = api_client.stix2.import_bundle_from_json(
8+
imported_malware_bundle, _ = api_client.stix2.import_bundle_from_json(
99
json_data=content
1010
)
1111
assert imported_malware_bundle is not None

tests/02-integration/utils/test_stix_crud.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def test_entity_create(entity_class, api_stix, opencti_splitter):
2121
stix_object = stix_class(**class_data)
2222
bundle = Bundle(objects=[stix_object]).serialize()
2323
split_bundle = opencti_splitter.split_bundle(bundle, True, None)[0]
24-
bundles_sent = api_stix.import_bundle_from_json(split_bundle, False, None, None)
24+
bundles_sent, _ = api_stix.import_bundle_from_json(split_bundle, False, None, None)
2525

2626
assert len(bundles_sent) == 1
2727
assert bundles_sent[0]["id"] == stix_object["id"]

0 commit comments

Comments
 (0)