pycti 6.3.13__tar.gz → 6.4.1__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 pycti might be problematic. Click here for more details.
- {pycti-6.3.13 → pycti-6.4.1}/PKG-INFO +1 -1
- {pycti-6.3.13 → pycti-6.4.1}/pycti/__init__.py +1 -1
- {pycti-6.3.13 → pycti-6.4.1}/pycti/api/opencti_api_client.py +31 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/connector/opencti_connector_helper.py +35 -8
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/indicator/opencti_indicator_properties.py +8 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_attack_pattern.py +6 -2
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_campaign.py +4 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_case_incident.py +4 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_case_rfi.py +4 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_case_rft.py +4 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_channel.py +4 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_course_of_action.py +5 -2
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_data_component.py +4 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_data_source.py +4 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_event.py +4 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_external_reference.py +6 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_feedback.py +4 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_grouping.py +9 -2
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_identity.py +5 -2
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_incident.py +4 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_indicator.py +4 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_infrastructure.py +4 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_intrusion_set.py +4 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_kill_chain_phase.py +4 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_language.py +4 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_location.py +22 -4
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_malware.py +4 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_malware_analysis.py +10 -2
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_marking_definition.py +6 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_narrative.py +4 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_note.py +12 -4
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_observed_data.py +4 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_opinion.py +19 -3
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_report.py +4 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_stix_core_relationship.py +22 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_stix_sighting_relationship.py +28 -5
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_task.py +5 -2
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_threat_actor.py +10 -3
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_threat_actor_group.py +4 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_threat_actor_individual.py +4 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_tool.py +4 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_vocabulary.py +15 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_vulnerability.py +4 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/utils/opencti_stix2.py +75 -50
- {pycti-6.3.13 → pycti-6.4.1}/pycti.egg-info/PKG-INFO +1 -1
- {pycti-6.3.13 → pycti-6.4.1}/LICENSE +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/README.md +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/api/__init__.py +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/api/opencti_api_connector.py +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/api/opencti_api_playbook.py +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/api/opencti_api_work.py +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/connector/__init__.py +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/connector/opencti_connector.py +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/connector/opencti_metric_handler.py +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/__init__.py +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/indicator/__init__.py +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_label.py +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_stix.py +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_stix_core_object.py +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_stix_cyber_observable.py +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_stix_domain_object.py +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_stix_nested_ref_relationship.py +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/opencti_stix_object_or_stix_relationship.py +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/stix_cyber_observable/__init__.py +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/stix_cyber_observable/opencti_stix_cyber_observable_deprecated.py +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/entities/stix_cyber_observable/opencti_stix_cyber_observable_properties.py +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/utils/__init__.py +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/utils/constants.py +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/utils/opencti_logger.py +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/utils/opencti_stix2_identifier.py +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/utils/opencti_stix2_splitter.py +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/utils/opencti_stix2_update.py +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti/utils/opencti_stix2_utils.py +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti.egg-info/SOURCES.txt +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti.egg-info/dependency_links.txt +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti.egg-info/requires.txt +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pycti.egg-info/top_level.txt +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/pyproject.toml +0 -0
- {pycti-6.3.13 → pycti-6.4.1}/setup.cfg +0 -0
|
@@ -213,6 +213,9 @@ class OpenCTIApiClient:
|
|
|
213
213
|
def set_event_id(self, event_id):
|
|
214
214
|
self.request_headers["opencti-event-id"] = event_id
|
|
215
215
|
|
|
216
|
+
def set_draft_id(self, draft_id):
|
|
217
|
+
self.request_headers["opencti-draft-id"] = draft_id
|
|
218
|
+
|
|
216
219
|
def set_synchronized_upsert_header(self, synchronized):
|
|
217
220
|
self.request_headers["synchronized-upsert"] = (
|
|
218
221
|
"true" if synchronized is True else "false"
|
|
@@ -666,6 +669,34 @@ class OpenCTIApiClient:
|
|
|
666
669
|
self.app_logger.error("[upload] Missing parameter: file_name")
|
|
667
670
|
return None
|
|
668
671
|
|
|
672
|
+
def create_draft(self, **kwargs):
|
|
673
|
+
"""create a draft in OpenCTI API
|
|
674
|
+
:param `**kwargs`: arguments for file name creating draft (required: `draft_name`)
|
|
675
|
+
:return: returns the query response for the draft creation
|
|
676
|
+
:rtype: id
|
|
677
|
+
"""
|
|
678
|
+
|
|
679
|
+
draft_name = kwargs.get("draft_name", None)
|
|
680
|
+
entity_id = kwargs.get("entity_id", None)
|
|
681
|
+
|
|
682
|
+
if draft_name is not None:
|
|
683
|
+
self.app_logger.info("Creating a draft.")
|
|
684
|
+
query = """
|
|
685
|
+
mutation draftWorkspaceAdd($input: DraftWorkspaceAddInput!) {
|
|
686
|
+
draftWorkspaceAdd(input: $input) {
|
|
687
|
+
id
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
"""
|
|
691
|
+
queryResult = self.query(
|
|
692
|
+
query,
|
|
693
|
+
{"input": {"name": draft_name, "entity_id": entity_id}},
|
|
694
|
+
)
|
|
695
|
+
return queryResult["data"]["draftWorkspaceAdd"]["id"]
|
|
696
|
+
else:
|
|
697
|
+
self.app_logger.error("[create_draft] Missing parameter: draft_name")
|
|
698
|
+
return None
|
|
699
|
+
|
|
669
700
|
def upload_pending_file(self, **kwargs):
|
|
670
701
|
"""upload a file to OpenCTI API
|
|
671
702
|
|
|
@@ -253,10 +253,18 @@ class ListenQueue(threading.Thread):
|
|
|
253
253
|
event_data = json_data["event"]
|
|
254
254
|
entity_id = event_data.get("entity_id")
|
|
255
255
|
entity_type = event_data.get("entity_type")
|
|
256
|
+
validation_mode = event_data.get("validation_mode", "workbench")
|
|
256
257
|
# Set the API headers
|
|
257
|
-
|
|
258
|
+
internal_data = json_data["internal"]
|
|
259
|
+
work_id = internal_data["work_id"]
|
|
260
|
+
draft_id = internal_data.get("draft_id", "")
|
|
258
261
|
self.helper.work_id = work_id
|
|
259
262
|
|
|
263
|
+
self.helper.validation_mode = validation_mode
|
|
264
|
+
self.helper.draft_id = draft_id
|
|
265
|
+
self.helper.api.set_draft_id(draft_id)
|
|
266
|
+
self.helper.api_impersonate.set_draft_id(draft_id)
|
|
267
|
+
|
|
260
268
|
self.helper.playbook = None
|
|
261
269
|
self.helper.enrichment_shared_organizations = None
|
|
262
270
|
if self.helper.connect_type == "INTERNAL_ENRICHMENT":
|
|
@@ -952,6 +960,8 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
|
|
|
952
960
|
"Connector registered with ID", {"id": self.connect_id}
|
|
953
961
|
)
|
|
954
962
|
self.work_id = None
|
|
963
|
+
self.validation_mode = "workbench"
|
|
964
|
+
self.draft_id = None
|
|
955
965
|
self.playbook = None
|
|
956
966
|
self.enrichment_shared_organizations = None
|
|
957
967
|
self.connector_id = connector_configuration["id"]
|
|
@@ -1550,6 +1560,7 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
|
|
|
1550
1560
|
"""send a stix2 bundle to the API
|
|
1551
1561
|
|
|
1552
1562
|
:param work_id: a valid work id
|
|
1563
|
+
:param draft_id: a draft context to send the bundle to
|
|
1553
1564
|
:param bundle: valid stix2 bundle
|
|
1554
1565
|
:type bundle:
|
|
1555
1566
|
:param entities_types: list of entities, defaults to None
|
|
@@ -1563,6 +1574,8 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
|
|
|
1563
1574
|
:rtype: list
|
|
1564
1575
|
"""
|
|
1565
1576
|
work_id = kwargs.get("work_id", self.work_id)
|
|
1577
|
+
validation_mode = kwargs.get("validation_mode", self.validation_mode)
|
|
1578
|
+
draft_id = kwargs.get("draft_id", self.draft_id)
|
|
1566
1579
|
entities_types = kwargs.get("entities_types", None)
|
|
1567
1580
|
update = kwargs.get("update", False)
|
|
1568
1581
|
event_version = kwargs.get("event_version", None)
|
|
@@ -1627,14 +1640,23 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
|
|
|
1627
1640
|
# Upload workbench in case of pending validation
|
|
1628
1641
|
if not file_name and work_id:
|
|
1629
1642
|
file_name = f"{work_id}.json"
|
|
1643
|
+
|
|
1630
1644
|
if self.connect_validate_before_import and not bypass_validation and file_name:
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1645
|
+
if validation_mode == "workbench":
|
|
1646
|
+
self.api.upload_pending_file(
|
|
1647
|
+
file_name=file_name,
|
|
1648
|
+
data=bundle,
|
|
1649
|
+
mime_type="application/json",
|
|
1650
|
+
entity_id=entity_id,
|
|
1651
|
+
)
|
|
1652
|
+
return []
|
|
1653
|
+
elif validation_mode == "draft" and not draft_id:
|
|
1654
|
+
draft_id = self.api.create_draft(
|
|
1655
|
+
draft_name=file_name, entity_id=entity_id
|
|
1656
|
+
)
|
|
1657
|
+
if not draft_id:
|
|
1658
|
+
self.connector_logger.error("Draft couldn't be created")
|
|
1659
|
+
return []
|
|
1638
1660
|
|
|
1639
1661
|
# If directory setup, write the bundle to the target directory
|
|
1640
1662
|
if bundle_send_to_directory and bundle_send_to_directory_path is not None:
|
|
@@ -1749,6 +1771,7 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
|
|
|
1749
1771
|
entities_types=entities_types,
|
|
1750
1772
|
sequence=sequence,
|
|
1751
1773
|
update=update,
|
|
1774
|
+
draft_id=draft_id,
|
|
1752
1775
|
)
|
|
1753
1776
|
channel.close()
|
|
1754
1777
|
pika_connection.close()
|
|
@@ -1774,11 +1797,14 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
|
|
|
1774
1797
|
:type entities_types: list, optional
|
|
1775
1798
|
:param update: whether to update data in the database, defaults to False
|
|
1776
1799
|
:type update: bool, optional
|
|
1800
|
+
:param draft_id: if draft_id is set, bundle must be set in draft context
|
|
1801
|
+
:type draft_id:
|
|
1777
1802
|
"""
|
|
1778
1803
|
work_id = kwargs.get("work_id", None)
|
|
1779
1804
|
sequence = kwargs.get("sequence", 0)
|
|
1780
1805
|
update = kwargs.get("update", False)
|
|
1781
1806
|
entities_types = kwargs.get("entities_types", None)
|
|
1807
|
+
draft_id = kwargs.get("draft_id", None)
|
|
1782
1808
|
|
|
1783
1809
|
if entities_types is None:
|
|
1784
1810
|
entities_types = []
|
|
@@ -1800,6 +1826,7 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
|
|
|
1800
1826
|
"utf-8"
|
|
1801
1827
|
),
|
|
1802
1828
|
"update": update,
|
|
1829
|
+
"draft_id": draft_id,
|
|
1803
1830
|
}
|
|
1804
1831
|
if work_id is not None:
|
|
1805
1832
|
message["work_id"] = work_id
|
|
@@ -92,6 +92,10 @@ INDICATOR_PROPERTIES = """
|
|
|
92
92
|
x_opencti_score
|
|
93
93
|
x_opencti_detection
|
|
94
94
|
x_opencti_main_observable_type
|
|
95
|
+
x_opencti_observable_values {
|
|
96
|
+
type
|
|
97
|
+
value
|
|
98
|
+
}
|
|
95
99
|
x_mitre_platforms
|
|
96
100
|
observables {
|
|
97
101
|
edges {
|
|
@@ -220,6 +224,10 @@ INDICATOR_PROPERTIES_WITH_FILES = """
|
|
|
220
224
|
x_opencti_score
|
|
221
225
|
x_opencti_detection
|
|
222
226
|
x_opencti_main_observable_type
|
|
227
|
+
x_opencti_observable_values {
|
|
228
|
+
type
|
|
229
|
+
value
|
|
230
|
+
}
|
|
223
231
|
x_mitre_platforms
|
|
224
232
|
observables {
|
|
225
233
|
edges {
|
|
@@ -222,15 +222,19 @@ class AttackPattern:
|
|
|
222
222
|
|
|
223
223
|
@staticmethod
|
|
224
224
|
def generate_id(name, x_mitre_id=None):
|
|
225
|
-
name = name.lower().strip()
|
|
226
225
|
if x_mitre_id is not None:
|
|
227
226
|
data = {"x_mitre_id": x_mitre_id}
|
|
228
227
|
else:
|
|
229
|
-
data = {"name": name}
|
|
228
|
+
data = {"name": name.lower().strip()}
|
|
230
229
|
data = canonicalize(data, utf8=False)
|
|
231
230
|
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
|
|
232
231
|
return "attack-pattern--" + id
|
|
233
232
|
|
|
233
|
+
@staticmethod
|
|
234
|
+
def generate_id_from_data(data):
|
|
235
|
+
external_id = data.get("x_mitre_id") or data.get("x_opencti_external_id")
|
|
236
|
+
return AttackPattern.generate_id(data.get("name"), external_id)
|
|
237
|
+
|
|
234
238
|
"""
|
|
235
239
|
List Attack-Pattern objects
|
|
236
240
|
|
|
@@ -216,6 +216,10 @@ class Campaign:
|
|
|
216
216
|
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
|
|
217
217
|
return "campaign--" + id
|
|
218
218
|
|
|
219
|
+
@staticmethod
|
|
220
|
+
def generate_id_from_data(data):
|
|
221
|
+
return Campaign.generate_id(data["name"])
|
|
222
|
+
|
|
219
223
|
"""
|
|
220
224
|
List Campaign objects
|
|
221
225
|
|
|
@@ -461,6 +461,10 @@ class CaseIncident:
|
|
|
461
461
|
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
|
|
462
462
|
return "case-incident--" + id
|
|
463
463
|
|
|
464
|
+
@staticmethod
|
|
465
|
+
def generate_id_from_data(data):
|
|
466
|
+
return CaseIncident.generate_id(data["name"], data["created"])
|
|
467
|
+
|
|
464
468
|
"""
|
|
465
469
|
List Case Incident objects
|
|
466
470
|
|
|
@@ -457,6 +457,10 @@ class CaseRfi:
|
|
|
457
457
|
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
|
|
458
458
|
return "case-rfi--" + id
|
|
459
459
|
|
|
460
|
+
@staticmethod
|
|
461
|
+
def generate_id_from_data(data):
|
|
462
|
+
return CaseRfi.generate_id(data["name"], data["created"])
|
|
463
|
+
|
|
460
464
|
"""
|
|
461
465
|
List Case Rfi objects
|
|
462
466
|
|
|
@@ -457,6 +457,10 @@ class CaseRft:
|
|
|
457
457
|
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
|
|
458
458
|
return "case-rft--" + id
|
|
459
459
|
|
|
460
|
+
@staticmethod
|
|
461
|
+
def generate_id_from_data(data):
|
|
462
|
+
return CaseRft.generate_id(data["name"], data["created"])
|
|
463
|
+
|
|
460
464
|
"""
|
|
461
465
|
List Case Rft objects
|
|
462
466
|
|
|
@@ -212,6 +212,10 @@ class Channel:
|
|
|
212
212
|
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
|
|
213
213
|
return "channel--" + id
|
|
214
214
|
|
|
215
|
+
@staticmethod
|
|
216
|
+
def generate_id_from_data(data):
|
|
217
|
+
return Channel.generate_id(data["name"])
|
|
218
|
+
|
|
215
219
|
"""
|
|
216
220
|
List Channel objects
|
|
217
221
|
|
|
@@ -196,15 +196,18 @@ class CourseOfAction:
|
|
|
196
196
|
|
|
197
197
|
@staticmethod
|
|
198
198
|
def generate_id(name, x_mitre_id=None):
|
|
199
|
-
name = name.lower().strip()
|
|
200
199
|
if x_mitre_id is not None:
|
|
201
200
|
data = {"x_mitre_id": x_mitre_id}
|
|
202
201
|
else:
|
|
203
|
-
data = {"name": name}
|
|
202
|
+
data = {"name": name.lower().strip()}
|
|
204
203
|
data = canonicalize(data, utf8=False)
|
|
205
204
|
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
|
|
206
205
|
return "course-of-action--" + id
|
|
207
206
|
|
|
207
|
+
@staticmethod
|
|
208
|
+
def generate_id_from_data(data):
|
|
209
|
+
return CourseOfAction.generate_id(data.get("name"), data.get("x_mitre_id"))
|
|
210
|
+
|
|
208
211
|
"""
|
|
209
212
|
List Course-Of-Action objects
|
|
210
213
|
|
|
@@ -246,6 +246,10 @@ class DataComponent:
|
|
|
246
246
|
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
|
|
247
247
|
return "data-component--" + id
|
|
248
248
|
|
|
249
|
+
@staticmethod
|
|
250
|
+
def generate_id_from_data(data):
|
|
251
|
+
return DataComponent.generate_id(data["name"])
|
|
252
|
+
|
|
249
253
|
"""
|
|
250
254
|
List Data-Component objects
|
|
251
255
|
|
|
@@ -204,6 +204,10 @@ class DataSource:
|
|
|
204
204
|
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
|
|
205
205
|
return "data-source--" + id
|
|
206
206
|
|
|
207
|
+
@staticmethod
|
|
208
|
+
def generate_id_from_data(data):
|
|
209
|
+
return DataSource.generate_id(data["name"])
|
|
210
|
+
|
|
207
211
|
"""
|
|
208
212
|
List Data-Source objects
|
|
209
213
|
|
|
@@ -216,6 +216,10 @@ class Event:
|
|
|
216
216
|
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
|
|
217
217
|
return "event--" + id
|
|
218
218
|
|
|
219
|
+
@staticmethod
|
|
220
|
+
def generate_id_from_data(data):
|
|
221
|
+
return Event.generate_id(data["name"])
|
|
222
|
+
|
|
219
223
|
"""
|
|
220
224
|
List Event objects
|
|
221
225
|
|
|
@@ -68,6 +68,12 @@ class ExternalReference:
|
|
|
68
68
|
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
|
|
69
69
|
return "external-reference--" + id
|
|
70
70
|
|
|
71
|
+
@staticmethod
|
|
72
|
+
def generate_id_from_data(data):
|
|
73
|
+
return ExternalReference.generate_id(
|
|
74
|
+
data.get("url"), data.get("source_name"), data.get("external_id")
|
|
75
|
+
)
|
|
76
|
+
|
|
71
77
|
"""
|
|
72
78
|
List External-Reference objects
|
|
73
79
|
|
|
@@ -419,6 +419,10 @@ class Feedback:
|
|
|
419
419
|
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
|
|
420
420
|
return "feedback--" + id
|
|
421
421
|
|
|
422
|
+
@staticmethod
|
|
423
|
+
def generate_id_from_data(data):
|
|
424
|
+
return Feedback.generate_id(data["name"])
|
|
425
|
+
|
|
422
426
|
"""
|
|
423
427
|
List Feedback objects
|
|
424
428
|
|
|
@@ -396,16 +396,23 @@ class Grouping:
|
|
|
396
396
|
"""
|
|
397
397
|
|
|
398
398
|
@staticmethod
|
|
399
|
-
def generate_id(name, context, created):
|
|
399
|
+
def generate_id(name, context, created=None):
|
|
400
400
|
name = name.lower().strip()
|
|
401
401
|
context = context.lower().strip()
|
|
402
402
|
if isinstance(created, datetime.datetime):
|
|
403
403
|
created = created.isoformat()
|
|
404
|
-
|
|
404
|
+
if created is None:
|
|
405
|
+
data = {"name": name, "context": context}
|
|
406
|
+
else:
|
|
407
|
+
data = {"name": name, "context": context, "created": created}
|
|
405
408
|
data = canonicalize(data, utf8=False)
|
|
406
409
|
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
|
|
407
410
|
return "grouping--" + id
|
|
408
411
|
|
|
412
|
+
@staticmethod
|
|
413
|
+
def generate_id_from_data(data):
|
|
414
|
+
return Grouping.generate_id(data["name"], data["context"], data["created"])
|
|
415
|
+
|
|
409
416
|
"""
|
|
410
417
|
List Grouping objects
|
|
411
418
|
|
|
@@ -226,12 +226,15 @@ class Identity:
|
|
|
226
226
|
|
|
227
227
|
@staticmethod
|
|
228
228
|
def generate_id(name, identity_class):
|
|
229
|
-
|
|
230
|
-
data = {"name": name, "identity_class": identity_class}
|
|
229
|
+
data = {"name": name.lower().strip(), "identity_class": identity_class.lower()}
|
|
231
230
|
data = canonicalize(data, utf8=False)
|
|
232
231
|
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
|
|
233
232
|
return "identity--" + id
|
|
234
233
|
|
|
234
|
+
@staticmethod
|
|
235
|
+
def generate_id_from_data(data):
|
|
236
|
+
return Identity.generate_id(data["name"], data["identity_class"])
|
|
237
|
+
|
|
235
238
|
"""
|
|
236
239
|
List Identity objects
|
|
237
240
|
|
|
@@ -225,6 +225,10 @@ class Incident:
|
|
|
225
225
|
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
|
|
226
226
|
return "incident--" + id
|
|
227
227
|
|
|
228
|
+
@staticmethod
|
|
229
|
+
def generate_id_from_data(data):
|
|
230
|
+
return Incident.generate_id(data["name"], data["created"])
|
|
231
|
+
|
|
228
232
|
"""
|
|
229
233
|
List Incident objects
|
|
230
234
|
|
|
@@ -29,6 +29,10 @@ class Indicator:
|
|
|
29
29
|
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
|
|
30
30
|
return "indicator--" + id
|
|
31
31
|
|
|
32
|
+
@staticmethod
|
|
33
|
+
def generate_id_from_data(data):
|
|
34
|
+
return Indicator.generate_id(data["pattern"])
|
|
35
|
+
|
|
32
36
|
def list(self, **kwargs):
|
|
33
37
|
"""List Indicator objects
|
|
34
38
|
|
|
@@ -239,6 +239,10 @@ class Infrastructure:
|
|
|
239
239
|
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
|
|
240
240
|
return "infrastructure--" + id
|
|
241
241
|
|
|
242
|
+
@staticmethod
|
|
243
|
+
def generate_id_from_data(data):
|
|
244
|
+
return Infrastructure.generate_id(data["name"])
|
|
245
|
+
|
|
242
246
|
def list(self, **kwargs):
|
|
243
247
|
"""List Infrastructure objects
|
|
244
248
|
|
|
@@ -222,6 +222,10 @@ class IntrusionSet:
|
|
|
222
222
|
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
|
|
223
223
|
return "intrusion-set--" + id
|
|
224
224
|
|
|
225
|
+
@staticmethod
|
|
226
|
+
def generate_id_from_data(data):
|
|
227
|
+
return IntrusionSet.generate_id(data["name"])
|
|
228
|
+
|
|
225
229
|
"""
|
|
226
230
|
List Intrusion-Set objects
|
|
227
231
|
|
|
@@ -28,6 +28,10 @@ class KillChainPhase:
|
|
|
28
28
|
phase_name=phase_name, kill_chain_name=kill_chain_name
|
|
29
29
|
)
|
|
30
30
|
|
|
31
|
+
@staticmethod
|
|
32
|
+
def generate_id_from_data(data):
|
|
33
|
+
return KillChainPhase.generate_id(data["phase_name"], data["kill_chain_name"])
|
|
34
|
+
|
|
31
35
|
"""
|
|
32
36
|
List Kill-Chain-Phase objects
|
|
33
37
|
|
|
@@ -224,6 +224,10 @@ class Language:
|
|
|
224
224
|
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
|
|
225
225
|
return "language--" + id
|
|
226
226
|
|
|
227
|
+
@staticmethod
|
|
228
|
+
def generate_id_from_data(data):
|
|
229
|
+
return Language.generate_id(data["name"])
|
|
230
|
+
|
|
227
231
|
"""
|
|
228
232
|
List Language objects
|
|
229
233
|
|
|
@@ -210,15 +210,33 @@ class Location:
|
|
|
210
210
|
|
|
211
211
|
@staticmethod
|
|
212
212
|
def generate_id(name, x_opencti_location_type, latitude=None, longitude=None):
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
213
|
+
if x_opencti_location_type == "Position":
|
|
214
|
+
if latitude is not None and longitude is None:
|
|
215
|
+
data = {"latitude": latitude}
|
|
216
|
+
elif latitude is None and longitude is not None:
|
|
217
|
+
data = {"longitude": longitude}
|
|
218
|
+
elif latitude is not None and longitude is not None:
|
|
219
|
+
data = {"latitude": latitude, "longitude": longitude}
|
|
220
|
+
else:
|
|
221
|
+
data = {"name": name.lower().strip()}
|
|
216
222
|
else:
|
|
217
|
-
data = {
|
|
223
|
+
data = {
|
|
224
|
+
"name": name.lower().strip(),
|
|
225
|
+
"x_opencti_location_type": x_opencti_location_type,
|
|
226
|
+
}
|
|
218
227
|
data = canonicalize(data, utf8=False)
|
|
219
228
|
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
|
|
220
229
|
return "location--" + id
|
|
221
230
|
|
|
231
|
+
@staticmethod
|
|
232
|
+
def generate_id_from_data(data):
|
|
233
|
+
return Location.generate_id(
|
|
234
|
+
data.get("name"),
|
|
235
|
+
data.get("x_opencti_location_type"),
|
|
236
|
+
data.get("latitude"),
|
|
237
|
+
data.get("longitude"),
|
|
238
|
+
)
|
|
239
|
+
|
|
222
240
|
"""
|
|
223
241
|
List Location objects
|
|
224
242
|
|
|
@@ -250,6 +250,10 @@ class Malware:
|
|
|
250
250
|
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
|
|
251
251
|
return "malware--" + id
|
|
252
252
|
|
|
253
|
+
@staticmethod
|
|
254
|
+
def generate_id_from_data(data):
|
|
255
|
+
return Malware.generate_id(data["name"])
|
|
256
|
+
|
|
253
257
|
"""
|
|
254
258
|
List Malware objects
|
|
255
259
|
|
|
@@ -219,13 +219,21 @@ class MalwareAnalysis:
|
|
|
219
219
|
"""
|
|
220
220
|
|
|
221
221
|
@staticmethod
|
|
222
|
-
def generate_id(result_name):
|
|
222
|
+
def generate_id(result_name, product=None, submitted=None):
|
|
223
223
|
result_name = result_name.lower().strip()
|
|
224
|
-
data = {"result_name": result_name}
|
|
224
|
+
data = {"result_name": result_name, "product": product}
|
|
225
|
+
if submitted is not None:
|
|
226
|
+
data = {**data, "submitted": submitted}
|
|
225
227
|
data = canonicalize(data, utf8=False)
|
|
226
228
|
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
|
|
227
229
|
return "malware-analysis--" + id
|
|
228
230
|
|
|
231
|
+
@staticmethod
|
|
232
|
+
def generate_id_from_data(data):
|
|
233
|
+
return MalwareAnalysis.generate_id(
|
|
234
|
+
data["result_name"], data["product"], data.get("submitted")
|
|
235
|
+
)
|
|
236
|
+
|
|
229
237
|
"""
|
|
230
238
|
List Malware analysis objects
|
|
231
239
|
|
|
@@ -31,6 +31,12 @@ class MarkingDefinition:
|
|
|
31
31
|
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
|
|
32
32
|
return "marking-definition--" + id
|
|
33
33
|
|
|
34
|
+
@staticmethod
|
|
35
|
+
def generate_id_from_data(data):
|
|
36
|
+
return MarkingDefinition.generate_id(
|
|
37
|
+
data["definition"], data["definition_type"]
|
|
38
|
+
)
|
|
39
|
+
|
|
34
40
|
"""
|
|
35
41
|
List Marking-Definition objects
|
|
36
42
|
|
|
@@ -202,6 +202,10 @@ class Narrative:
|
|
|
202
202
|
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
|
|
203
203
|
return "narrative--" + id
|
|
204
204
|
|
|
205
|
+
@staticmethod
|
|
206
|
+
def generate_id_from_data(data):
|
|
207
|
+
return Narrative.generate_id(data["name"])
|
|
208
|
+
|
|
205
209
|
"""
|
|
206
210
|
List Narrative objects
|
|
207
211
|
|
|
@@ -436,14 +436,22 @@ class Note:
|
|
|
436
436
|
|
|
437
437
|
@staticmethod
|
|
438
438
|
def generate_id(created, content):
|
|
439
|
-
content
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
439
|
+
if content is None:
|
|
440
|
+
raise ValueError("content is required")
|
|
441
|
+
if created is not None:
|
|
442
|
+
if isinstance(created, datetime.datetime):
|
|
443
|
+
created = created.isoformat()
|
|
444
|
+
data = {"content": content, "created": created}
|
|
445
|
+
else:
|
|
446
|
+
data = {"content": content}
|
|
443
447
|
data = canonicalize(data, utf8=False)
|
|
444
448
|
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
|
|
445
449
|
return "note--" + id
|
|
446
450
|
|
|
451
|
+
@staticmethod
|
|
452
|
+
def generate_id_from_data(data):
|
|
453
|
+
return Note.generate_id(data.get("created"), data["content"])
|
|
454
|
+
|
|
447
455
|
"""
|
|
448
456
|
List Note objects
|
|
449
457
|
|
|
@@ -439,6 +439,10 @@ class ObservedData:
|
|
|
439
439
|
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
|
|
440
440
|
return "observed-data--" + id
|
|
441
441
|
|
|
442
|
+
@staticmethod
|
|
443
|
+
def generate_id_from_data(data):
|
|
444
|
+
return ObservedData.generate_id(data["object_refs"])
|
|
445
|
+
|
|
442
446
|
"""
|
|
443
447
|
List ObservedData objects
|
|
444
448
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# coding: utf-8
|
|
2
|
-
|
|
2
|
+
import datetime
|
|
3
3
|
import json
|
|
4
4
|
import uuid
|
|
5
5
|
|
|
6
|
+
from stix2.canonicalization.Canonicalize import canonicalize
|
|
7
|
+
|
|
6
8
|
|
|
7
9
|
class Opinion:
|
|
8
10
|
def __init__(self, opencti):
|
|
@@ -211,8 +213,22 @@ class Opinion:
|
|
|
211
213
|
"""
|
|
212
214
|
|
|
213
215
|
@staticmethod
|
|
214
|
-
def generate_id():
|
|
215
|
-
|
|
216
|
+
def generate_id(created, opinion):
|
|
217
|
+
if opinion is None:
|
|
218
|
+
raise ValueError("opinion is required")
|
|
219
|
+
if created is not None:
|
|
220
|
+
if isinstance(created, datetime.datetime):
|
|
221
|
+
created = created.isoformat()
|
|
222
|
+
data = {"opinion": opinion, "created": created}
|
|
223
|
+
else:
|
|
224
|
+
data = {"opinion": opinion}
|
|
225
|
+
data = canonicalize(data, utf8=False)
|
|
226
|
+
id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data))
|
|
227
|
+
return "opinion--" + id
|
|
228
|
+
|
|
229
|
+
@staticmethod
|
|
230
|
+
def generate_id_from_data(data):
|
|
231
|
+
return Opinion.generate_id(data.get("created"), data["opinion"])
|
|
216
232
|
|
|
217
233
|
"""
|
|
218
234
|
List Opinion objects
|