stix2arango 1.1.4__tar.gz → 1.1.6__tar.gz

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 stix2arango might be problematic. Click here for more details.

Files changed (109) hide show
  1. {stix2arango-1.1.4 → stix2arango-1.1.6}/PKG-INFO +1 -1
  2. {stix2arango-1.1.4 → stix2arango-1.1.6}/pyproject.toml +1 -1
  3. {stix2arango-1.1.4 → stix2arango-1.1.6}/stix2arango/stix2arango/bundle_loader.py +38 -21
  4. {stix2arango-1.1.4 → stix2arango-1.1.6}/stix2arango/utils.py +3 -0
  5. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/test_bundle_loader.py +44 -14
  6. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/test_utils.py +16 -0
  7. {stix2arango-1.1.4 → stix2arango-1.1.6}/.env.example +0 -0
  8. {stix2arango-1.1.4 → stix2arango-1.1.6}/.env.markdown +0 -0
  9. {stix2arango-1.1.4 → stix2arango-1.1.6}/.github/workflows/create-release.yml +0 -0
  10. {stix2arango-1.1.4 → stix2arango-1.1.6}/.github/workflows/run-tests.yml +0 -0
  11. {stix2arango-1.1.4 → stix2arango-1.1.6}/.gitignore +0 -0
  12. {stix2arango-1.1.4 → stix2arango-1.1.6}/LICENSE +0 -0
  13. {stix2arango-1.1.4 → stix2arango-1.1.6}/README.md +0 -0
  14. {stix2arango-1.1.4 → stix2arango-1.1.6}/docs/README.md +0 -0
  15. {stix2arango-1.1.4 → stix2arango-1.1.6}/docs/stix2arango.png +0 -0
  16. {stix2arango-1.1.4 → stix2arango-1.1.6}/requirements.txt +0 -0
  17. {stix2arango-1.1.4 → stix2arango-1.1.6}/stix2arango/__init__.py +0 -0
  18. {stix2arango-1.1.4 → stix2arango-1.1.6}/stix2arango/__main__.py +0 -0
  19. {stix2arango-1.1.4 → stix2arango-1.1.6}/stix2arango/config.py +0 -0
  20. {stix2arango-1.1.4 → stix2arango-1.1.6}/stix2arango/services/__init__.py +0 -0
  21. {stix2arango-1.1.4 → stix2arango-1.1.6}/stix2arango/services/arangodb_service.py +0 -0
  22. {stix2arango-1.1.4 → stix2arango-1.1.6}/stix2arango/services/version_annotator.py +0 -0
  23. {stix2arango-1.1.4 → stix2arango-1.1.6}/stix2arango/stix2arango/__init__.py +0 -0
  24. {stix2arango-1.1.4 → stix2arango-1.1.6}/stix2arango/stix2arango/stix2arango.py +0 -0
  25. {stix2arango-1.1.4 → stix2arango-1.1.6}/stix2arango/templates/marking-definition.json +0 -0
  26. {stix2arango-1.1.4 → stix2arango-1.1.6}/stix2arango.py +0 -0
  27. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/README.md +0 -0
  28. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/custom-sco-original.json +0 -0
  29. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/custom-sco-updated.json +0 -0
  30. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/custom-sdo-original.json +0 -0
  31. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/custom-sdo-updated.json +0 -0
  32. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/duplicate-objects-all-properties-same.json +0 -0
  33. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/duplicate-objects-properties-different.json +0 -0
  34. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/embedded-ref-object-removed.json +0 -0
  35. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/embedded-ref-object-updated.json +0 -0
  36. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/embedded-ref-object.json +0 -0
  37. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/embedded-ref-sdo-sco-sro-smo.json +0 -0
  38. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/nested-embedded-ref.json +0 -0
  39. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/non-standard-embedded-relationship.json +0 -0
  40. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/sco-original.json +0 -0
  41. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/sco-updated.json +0 -0
  42. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/sigma-rule-bundle-another.json +0 -0
  43. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/sigma-rule-bundle-condensed-original.json +0 -0
  44. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/sigma-rule-bundle-condensed-update-1.json +0 -0
  45. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/sigma-rule-bundle-condensed-update-2.json +0 -0
  46. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/sigma-rule-bundle-yet-another.json +0 -0
  47. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/sigma-rule-bundle.json +0 -0
  48. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/smo-embedded-ref-1.json +0 -0
  49. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/smo-embedded-ref-2.json +0 -0
  50. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/smo-original.json +0 -0
  51. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/smo-updated-2.json +0 -0
  52. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/smo-updated.json +0 -0
  53. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/source-object-does-not-exist.json +0 -0
  54. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/sro-original.json +0 -0
  55. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/sro-updated.json +0 -0
  56. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/target-object-does-not-exist.json +0 -0
  57. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/update_with_diff_modified_times_1.json +0 -0
  58. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/files/stix2arango/update_with_diff_modified_times_2.json +0 -0
  59. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/requirements.txt +0 -0
  60. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/__init__.py +0 -0
  61. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/base_test.py +0 -0
  62. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/test_00-basic-import-logic-with-embedded.py +0 -0
  63. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/test_01-basic-import-no-embedded.py +0 -0
  64. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/test_02-basic-import-no-stix2arango_note.py +0 -0
  65. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/test_03-import-with-no-update-detected.py +0 -0
  66. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/test_04-update-detected-because-of-stix2arango-note.py +0 -0
  67. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/test_05-update-detected-because-of-modified-time-change.py +0 -0
  68. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/test_06-update-detected-because-of-modified-time-change-not-in-order.py +0 -0
  69. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/test_07-test-default-imported-objects.py +0 -0
  70. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/test_08-duplicate-objects-in-bundle-all-identical.py +0 -0
  71. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/test_09-duplicate-objects-but-diff-times-in-bundle.py +0 -0
  72. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/test_10-updating-object-no-modified-time.py +0 -0
  73. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/test_11-custom-sdos.py +0 -0
  74. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/test_12-custom-scos.py +0 -0
  75. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/test_13-updating-smos.py +0 -0
  76. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/test_14-non-standard-embedded-relationship.py +0 -0
  77. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/test_15-testing-when-sro-target-not-in-collection.py +0 -0
  78. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/test_16-testing-when-sro-source-not-in-collection.py +0 -0
  79. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/test_17-testing-update-of-embedded-refs.py +0 -0
  80. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/test_18-testing-nested-embedded-ref.py +0 -0
  81. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/test_19_update_with_diff_created_times.py +0 -0
  82. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/test_20-embedded-sro-update-for-sdo.py +0 -0
  83. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/test_21-embedded-sro-update-for-sco.py +0 -0
  84. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/test_22-embedded-sro-update-for-smo.py +0 -0
  85. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/test_23-embedded-sro-removed.py +0 -0
  86. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/test_24-test-hidden-properties.py +0 -0
  87. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/test_25-ignore-embedded-rels-smo.py +0 -0
  88. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/full_tests/test_26-ignore-embedded-rels-sro.py +0 -0
  89. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/test_arango_service.py +0 -0
  90. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/test_main.py +0 -0
  91. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/src/test_stix2arango.py +0 -0
  92. {stix2arango-1.1.4 → stix2arango-1.1.6}/tests/tests.env +0 -0
  93. {stix2arango-1.1.4 → stix2arango-1.1.6}/utilities/arango_cti_processor/README.md +0 -0
  94. {stix2arango-1.1.4 → stix2arango-1.1.6}/utilities/arango_cti_processor/insert_archive_atlas.py +0 -0
  95. {stix2arango-1.1.4 → stix2arango-1.1.6}/utilities/arango_cti_processor/insert_archive_attack_enterprise.py +0 -0
  96. {stix2arango-1.1.4 → stix2arango-1.1.6}/utilities/arango_cti_processor/insert_archive_attack_ics.py +0 -0
  97. {stix2arango-1.1.4 → stix2arango-1.1.6}/utilities/arango_cti_processor/insert_archive_attack_mobile.py +0 -0
  98. {stix2arango-1.1.4 → stix2arango-1.1.6}/utilities/arango_cti_processor/insert_archive_capec.py +0 -0
  99. {stix2arango-1.1.4 → stix2arango-1.1.6}/utilities/arango_cti_processor/insert_archive_cwe.py +0 -0
  100. {stix2arango-1.1.4 → stix2arango-1.1.6}/utilities/arango_cti_processor/insert_archive_disarm.py +0 -0
  101. {stix2arango-1.1.4 → stix2arango-1.1.6}/utilities/arango_cti_processor/insert_archive_locations.py +0 -0
  102. {stix2arango-1.1.4 → stix2arango-1.1.6}/utilities/arango_cti_processor/insert_archive_sigma_rules.py +0 -0
  103. {stix2arango-1.1.4 → stix2arango-1.1.6}/utilities/arango_cti_processor/insert_archive_tlp.py +0 -0
  104. {stix2arango-1.1.4 → stix2arango-1.1.6}/utilities/arango_cti_processor/insert_archive_yara_rules.py +0 -0
  105. {stix2arango-1.1.4 → stix2arango-1.1.6}/utilities/arango_cve_processor/README.md +0 -0
  106. {stix2arango-1.1.4 → stix2arango-1.1.6}/utilities/arango_cve_processor/insert_archive_cve.py +0 -0
  107. {stix2arango-1.1.4 → stix2arango-1.1.6}/utilities/arango_cve_processor/manager.py +0 -0
  108. {stix2arango-1.1.4 → stix2arango-1.1.6}/utilities/stix2arango/add_ref_types.py +0 -0
  109. {stix2arango-1.1.4 → stix2arango-1.1.6}/utilities/stix2arango/update_taxii_index_with_date_transform.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stix2arango
3
- Version: 1.1.4
3
+ Version: 1.1.6
4
4
  Summary: stix2arango is a command line tool that takes a group of STIX 2.1 objects in a bundle and inserts them into ArangoDB. It can also handle updates to existing objects in ArangoDB imported in a bundle.
5
5
  Project-URL: Homepage, https://github.com/muchdogesec/stix2arango
6
6
  Project-URL: Issues, https://github.com/muchdogesec/stix2arango/issues
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "stix2arango"
7
- version = "1.1.4"
7
+ version = "1.1.6"
8
8
  authors = [
9
9
  { name = "dogesec" }
10
10
  ]
@@ -10,6 +10,9 @@ import ijson
10
10
  import json
11
11
  from collections import Counter
12
12
 
13
+ from stix2arango.utils import get_embedded_refs
14
+
15
+
13
16
  class BundleLoader:
14
17
  def __init__(self, file_path, chunk_size_min=20_000, db_path=""):
15
18
  self.file_path = Path(file_path)
@@ -19,34 +22,37 @@ class BundleLoader:
19
22
 
20
23
  self.db_path = db_path
21
24
  if not self.db_path:
22
- self.temp_path = tempfile.NamedTemporaryFile(prefix='s2a_bundle_loader--', suffix='.sqlite')
25
+ self.temp_path = tempfile.NamedTemporaryFile(
26
+ prefix="s2a_bundle_loader--", suffix=".sqlite"
27
+ )
23
28
  self.db_path = self.temp_path.name
24
29
  self._init_db()
25
30
 
26
31
  def _init_db(self):
27
32
  """Initialize SQLite DB with objects table."""
28
33
  self.conn = sqlite3.connect(self.db_path)
29
- self.conn.execute('''
34
+ self.conn.execute(
35
+ """
30
36
  CREATE TABLE IF NOT EXISTS objects (
31
37
  id TEXT PRIMARY KEY,
32
38
  type TEXT,
33
39
  raw TEXT
34
40
  )
35
- ''')
36
- self.conn.execute('PRAGMA synchronous = OFF;')
37
- self.conn.execute('PRAGMA journal_mode = MEMORY;')
38
- self.conn.execute('PRAGMA temp_store = MEMORY;')
41
+ """
42
+ )
43
+ self.conn.execute("PRAGMA synchronous = OFF;")
44
+ self.conn.execute("PRAGMA journal_mode = MEMORY;")
45
+ self.conn.execute("PRAGMA temp_store = MEMORY;")
39
46
  self.conn.commit()
40
47
 
41
-
42
48
  def save_to_sqlite(self, objects):
43
49
  """Save one STIX object to the SQLite database."""
44
- self.inserted = getattr(self, 'inserted', 0)
50
+ self.inserted = getattr(self, "inserted", 0)
45
51
 
46
52
  try:
47
53
  self.conn.executemany(
48
54
  "INSERT OR REPLACE INTO objects (id, type, raw) VALUES (?, ?, ?)",
49
- [(obj['id'], obj['type'], json.dumps(obj)) for obj in objects]
55
+ [(obj["id"], obj["type"], json.dumps(obj)) for obj in objects],
50
56
  )
51
57
  except sqlite3.IntegrityError as e:
52
58
  print(f"Failed to insert len({objects}) objects: {e}")
@@ -55,6 +61,13 @@ class BundleLoader:
55
61
  self.inserted += len(objects)
56
62
  # logging.info(f"inserted {self.inserted}")
57
63
 
64
+ @staticmethod
65
+ def get_refs(obj):
66
+ refs = []
67
+ for _type, targets in get_embedded_refs(obj):
68
+ refs.extend(targets)
69
+ return refs
70
+
58
71
  def build_groups(self):
59
72
  """
60
73
  Iterates the STIX bundle and uses union-find to group IDs such that for every
@@ -63,30 +76,35 @@ class BundleLoader:
63
76
  """
64
77
  all_ids: dict[str, list[str]] = dict() # All object IDs in the file
65
78
  logging.info(f"loading into {self.db_path}")
66
-
67
- with open(self.file_path, 'rb') as f:
68
- objects = ijson.items(f, 'objects.item', use_float=True)
79
+
80
+ with open(self.file_path, "rb") as f:
81
+ objects = ijson.items(f, "objects.item", use_float=True)
69
82
  to_insert = []
70
83
  for obj in objects:
71
- obj_id = obj.get('id')
84
+ obj_id = obj.get("id")
72
85
  to_insert.append(obj)
73
86
  all_ids.setdefault(obj_id, [])
74
- if obj['type'] == 'relationship' and all(x in obj for x in ['target_ref', 'source_ref']):
75
- sr, tr = [obj['source_ref'], obj['target_ref']]
87
+ if obj["type"] == "relationship" and all(
88
+ x in obj for x in ["target_ref", "source_ref"]
89
+ ):
90
+ sr, tr = [obj["source_ref"], obj["target_ref"]]
76
91
  all_ids[obj_id].extend([sr, tr])
77
92
  all_ids.setdefault(sr, []).extend([tr, obj_id])
78
93
  all_ids.setdefault(tr, []).extend([sr, obj_id])
94
+ for ref in self.get_refs(obj):
95
+ all_ids[obj_id].append(ref)
79
96
  if len(to_insert) >= self.chunk_size_min:
80
97
  self.save_to_sqlite(to_insert)
81
98
  to_insert.clear()
82
99
  if to_insert:
83
100
  self.save_to_sqlite(to_insert)
84
-
101
+
85
102
  logging.info(f"loaded {self.inserted} into {self.db_path}")
86
103
  handled = set()
87
104
 
88
105
  self.groups = []
89
106
  group = set()
107
+
90
108
  def from_ids(all_ids):
91
109
  for obj_id in all_ids:
92
110
  if obj_id in handled:
@@ -104,18 +122,17 @@ class BundleLoader:
104
122
  if group:
105
123
  self.groups.append(tuple(group))
106
124
  return self.groups
107
-
125
+
108
126
  def load_objects_by_ids(self, ids):
109
127
  """Retrieve a list of STIX objects by their IDs from the SQLite database."""
110
- placeholders = ','.join(['?'] * len(ids))
128
+ placeholders = ",".join(["?"] * len(ids))
111
129
  query = f"SELECT raw FROM objects WHERE id IN ({placeholders})"
112
130
  cursor = self.conn.execute(query, list(ids))
113
131
  return [json.loads(row[0]) for row in cursor.fetchall()]
114
132
 
115
-
116
133
  def get_objects(self, group):
117
134
  return list(self.load_objects_by_ids(group))
118
-
135
+
119
136
  @property
120
137
  def chunks(self):
121
138
  for group in self.groups or self.build_groups():
@@ -123,4 +140,4 @@ class BundleLoader:
123
140
 
124
141
  def __del__(self):
125
142
  with contextlib.suppress(Exception):
126
- os.remove(self.db_path)
143
+ os.remove(self.db_path)
@@ -125,6 +125,9 @@ def get_embedded_refs(object: list | dict, xpath: list = [], attributes=None):
125
125
  if match := EMBEDDED_RELATIONSHIP_RE.fullmatch(key):
126
126
  relationship_type = "-".join(xpath + match.group(1).split("_"))
127
127
  targets = value if isinstance(value, list) else [value]
128
+ targets = [_target for _target in targets if _target and isinstance(_target, str)]
129
+ if not targets:
130
+ continue
128
131
  if attributes and key not in attributes:
129
132
  continue
130
133
  embedded_refs.append((relationship_type, targets))
@@ -5,31 +5,51 @@ import tempfile
5
5
  from pathlib import Path
6
6
  from unittest.mock import patch
7
7
 
8
- from stix2arango.stix2arango.bundle_loader import BundleLoader # adjust this import path
8
+ from stix2arango.stix2arango.bundle_loader import (
9
+ BundleLoader,
10
+ ) # adjust this import path
9
11
 
10
12
  # Sample bundle with related and unrelated objects
11
13
  STIX_BUNDLE = {
12
14
  "type": "bundle",
13
15
  "id": "bundle--example",
14
16
  "objects": [
15
- {"id": "indicator--1", "type": "indicator"},
16
- {"id": "indicator--2", "type": "indicator"},
17
- {"id": "relationship--1", "type": "relationship", "source_ref": "indicator--1", "target_ref": "indicator--2"},
17
+ {
18
+ "id": "indicator--1",
19
+ "type": "indicator",
20
+ "bad_ref": "some-ref--7",
21
+ "created_by_ref": "creator--1",
22
+ },
23
+ {
24
+ "id": "indicator--2",
25
+ "type": "indicator",
26
+ "object_marking_refs": ["marking--1", "marking--2"],
27
+ },
28
+ {
29
+ "id": "relationship--1",
30
+ "type": "relationship",
31
+ "source_ref": "indicator--1",
32
+ "target_ref": "indicator--2",
33
+ },
18
34
  {"id": "attack-pattern--3", "type": "attack-pattern"},
19
- ]
35
+ {"id": "marking--1", "type": "marking-definition"},
36
+ ],
20
37
  }
21
38
 
39
+
22
40
  @pytest.fixture
23
41
  def temp_json_file():
24
- with tempfile.NamedTemporaryFile(delete=False, mode='w', suffix='.json') as f:
42
+ with tempfile.NamedTemporaryFile(delete=False, mode="w", suffix=".json") as f:
25
43
  json.dump(STIX_BUNDLE, f)
26
44
  return f.name
27
45
 
46
+
28
47
  def test_init_db_and_tempfile_creation():
29
48
  loader = BundleLoader(file_path=tempfile.mkstemp()[1])
30
49
  assert isinstance(loader.db_path, str)
31
50
  assert Path(loader.db_path).exists()
32
51
 
52
+
33
53
  def test_save_to_sqlite_inserts_objects(temp_json_file):
34
54
  loader = BundleLoader(file_path=temp_json_file)
35
55
  objects = STIX_BUNDLE["objects"]
@@ -39,17 +59,24 @@ def test_save_to_sqlite_inserts_objects(temp_json_file):
39
59
  count = cursor.fetchone()[0]
40
60
  assert count == len(objects)
41
61
 
62
+
42
63
  def test_build_groups_creates_correct_groups(temp_json_file):
43
- loader = BundleLoader(file_path=temp_json_file, chunk_size_min=1)
64
+ loader = BundleLoader(file_path=temp_json_file, chunk_size_min=3)
44
65
  groups = loader.build_groups()
45
66
 
46
67
  # There should be at least 2 groups: one for related indicators, one for attack-pattern
47
68
  assert isinstance(groups, list)
48
69
  flat = [id_ for group in groups for id_ in group]
49
- assert "indicator--1" in flat
50
- assert "indicator--2" in flat
51
- assert "relationship--1" in flat
52
- assert "attack-pattern--3" in flat
70
+ assert set(flat) == {
71
+ "some-ref--7",
72
+ "indicator--2",
73
+ "indicator--1",
74
+ "relationship--1",
75
+ "creator--1",
76
+ "attack-pattern--3",
77
+ "marking--1",
78
+ }
79
+
53
80
 
54
81
  def test_load_objects_by_ids(temp_json_file):
55
82
  loader = BundleLoader(file_path=temp_json_file)
@@ -59,6 +86,7 @@ def test_load_objects_by_ids(temp_json_file):
59
86
  assert isinstance(result, list)
60
87
  assert {obj["id"] for obj in result} == {"indicator--1", "attack-pattern--3"}
61
88
 
89
+
62
90
  def test_get_objects_returns_objects(temp_json_file):
63
91
  loader = BundleLoader(file_path=temp_json_file)
64
92
  loader.save_to_sqlite(STIX_BUNDLE["objects"])
@@ -67,6 +95,7 @@ def test_get_objects_returns_objects(temp_json_file):
67
95
  assert isinstance(result, list)
68
96
  assert result[0]["id"] == "indicator--1"
69
97
 
98
+
70
99
  def test_chunks_generator(temp_json_file):
71
100
  loader = BundleLoader(file_path=temp_json_file, chunk_size_min=1)
72
101
  chunks = list(loader.chunks)
@@ -76,18 +105,20 @@ def test_chunks_generator(temp_json_file):
76
105
  assert isinstance(chunk, list)
77
106
  assert all("id" in obj for obj in chunk)
78
107
 
108
+
79
109
  def test_chunks_with_no_existing_groups_calls_build_groups(temp_json_file):
80
110
  loader = BundleLoader(file_path=temp_json_file, chunk_size_min=1)
81
- with patch.object(loader, 'build_groups', wraps=loader.build_groups) as mocked:
111
+ with patch.object(loader, "build_groups", wraps=loader.build_groups) as mocked:
82
112
  chunks = list(loader.chunks)
83
113
  mocked.assert_called_once()
84
114
  assert len(chunks) > 0
85
115
 
116
+
86
117
  def test_no_crash_on_missing_relationship_fields(temp_json_file):
87
118
  # Remove `source_ref` and `target_ref` to simulate bad data
88
119
  bad_bundle = {
89
120
  "type": "bundle",
90
- "objects": [{"id": "bad--1", "type": "relationship"}]
121
+ "objects": [{"id": "bad--1", "type": "relationship"}],
91
122
  }
92
123
  with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".json") as f:
93
124
  json.dump(bad_bundle, f)
@@ -104,4 +135,3 @@ def test_sqlite_file_removed_after_gc(temp_json_file):
104
135
  assert db_path.exists()
105
136
  del loader
106
137
  assert not db_path.exists(), "should already be deleted"
107
-
@@ -52,6 +52,22 @@ def test_get_embedded_refs():
52
52
  ("abcde-abcd-efgh", ["ref8"]),
53
53
  ]
54
54
 
55
+ def test_get_embedded_refs_empty():
56
+ assert utils.get_embedded_refs(
57
+ {
58
+ "abc_ref": "ref1",
59
+ "empty_ref": "", #skipped entirely
60
+ "some_empty_refs": ["ref10", "", "ref9"], # empty ref skipped
61
+ "abcd_refs": ["ref1", "ref2"],
62
+ "abcde": [{"abcdef_ref": "ref7"}, {"abcd_efgh_ref": "ref8"}],
63
+ }
64
+ ) == [
65
+ ("abc", ["ref1"]),
66
+ ("some-empty", ["ref10", "ref9"]),
67
+ ("abcd", ["ref1", "ref2"]),
68
+ ("abcde-abcdef", ["ref7"]),
69
+ ("abcde-abcd-efgh", ["ref8"]),
70
+ ]
55
71
 
56
72
  def test_get_embedded_refs__attributes_whitelist():
57
73
  assert utils.get_embedded_refs(
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes