pycti 6.7.20__py3-none-any.whl → 6.8.0__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 +1 -1
- pycti/connector/opencti_connector_helper.py +190 -3
- pycti/connector/opencti_metric_handler.py +44 -15
- pycti/entities/opencti_attack_pattern.py +32 -6
- pycti/entities/opencti_campaign.py +30 -6
- pycti/entities/opencti_case_incident.py +7 -0
- pycti/entities/opencti_case_rfi.py +7 -0
- pycti/entities/opencti_case_rft.py +7 -0
- pycti/entities/opencti_channel.py +7 -0
- pycti/entities/opencti_course_of_action.py +7 -0
- pycti/entities/opencti_data_component.py +7 -0
- pycti/entities/opencti_data_source.py +7 -0
- pycti/entities/opencti_event.py +32 -6
- pycti/entities/opencti_external_reference.py +8 -0
- pycti/entities/opencti_feedback.py +7 -0
- pycti/entities/opencti_grouping.py +7 -0
- pycti/entities/opencti_identity.py +34 -6
- pycti/entities/opencti_incident.py +7 -0
- pycti/entities/opencti_indicator.py +19 -5
- pycti/entities/opencti_intrusion_set.py +58 -19
- pycti/entities/opencti_kill_chain_phase.py +7 -0
- pycti/entities/opencti_label.py +7 -0
- pycti/entities/opencti_language.py +7 -0
- pycti/entities/opencti_location.py +7 -0
- pycti/entities/opencti_malware.py +60 -18
- pycti/entities/opencti_malware_analysis.py +7 -0
- pycti/entities/opencti_marking_definition.py +7 -0
- pycti/entities/opencti_narrative.py +7 -0
- pycti/entities/opencti_note.py +7 -0
- pycti/entities/opencti_observed_data.py +7 -0
- pycti/entities/opencti_opinion.py +7 -0
- pycti/entities/opencti_report.py +7 -0
- pycti/entities/opencti_stix.py +7 -0
- pycti/entities/opencti_stix_core_object.py +8 -0
- pycti/entities/opencti_stix_core_relationship.py +7 -0
- pycti/entities/opencti_stix_cyber_observable.py +9 -0
- pycti/entities/opencti_stix_domain_object.py +8 -0
- pycti/entities/opencti_stix_nested_ref_relationship.py +7 -0
- pycti/entities/opencti_stix_object_or_stix_relationship.py +7 -0
- pycti/entities/opencti_stix_sighting_relationship.py +7 -0
- pycti/entities/opencti_task.py +7 -0
- pycti/entities/opencti_tool.py +52 -18
- pycti/entities/opencti_vocabulary.py +7 -0
- pycti/entities/opencti_vulnerability.py +7 -0
- pycti/utils/opencti_logger.py +57 -0
- pycti/utils/opencti_stix2.py +41 -0
- pycti/utils/opencti_stix2_splitter.py +5 -0
- pycti/utils/opencti_stix2_utils.py +41 -6
- {pycti-6.7.20.dist-info → pycti-6.8.0.dist-info}/METADATA +1 -1
- pycti-6.8.0.dist-info/RECORD +86 -0
- pycti-6.7.20.dist-info/RECORD +0 -86
- {pycti-6.7.20.dist-info → pycti-6.8.0.dist-info}/WHEEL +0 -0
- {pycti-6.7.20.dist-info → pycti-6.8.0.dist-info}/licenses/LICENSE +0 -0
- {pycti-6.7.20.dist-info → pycti-6.8.0.dist-info}/top_level.txt +0 -0
pycti/__init__.py
CHANGED
|
@@ -37,10 +37,21 @@ app = FastAPI()
|
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
def killProgramHook(etype, value, tb):
|
|
40
|
+
"""Exception hook to terminate the program.
|
|
41
|
+
|
|
42
|
+
:param etype: Exception type
|
|
43
|
+
:param value: Exception value
|
|
44
|
+
:param tb: Traceback object
|
|
45
|
+
"""
|
|
40
46
|
os.kill(os.getpid(), signal.SIGTERM)
|
|
41
47
|
|
|
42
48
|
|
|
43
49
|
def start_loop(loop):
|
|
50
|
+
"""Start an asyncio event loop.
|
|
51
|
+
|
|
52
|
+
:param loop: The asyncio event loop to start
|
|
53
|
+
:type loop: asyncio.AbstractEventLoop
|
|
54
|
+
"""
|
|
44
55
|
asyncio.set_event_loop(loop)
|
|
45
56
|
loop.run_forever()
|
|
46
57
|
|
|
@@ -93,10 +104,24 @@ def get_config_variable(
|
|
|
93
104
|
|
|
94
105
|
|
|
95
106
|
def is_memory_certificate(certificate):
|
|
107
|
+
"""Check if a certificate is provided as a PEM string in memory.
|
|
108
|
+
|
|
109
|
+
:param certificate: The certificate data to check
|
|
110
|
+
:type certificate: str
|
|
111
|
+
:return: True if the certificate is a PEM string, False otherwise
|
|
112
|
+
:rtype: bool
|
|
113
|
+
"""
|
|
96
114
|
return certificate.startswith("-----BEGIN")
|
|
97
115
|
|
|
98
116
|
|
|
99
117
|
def ssl_verify_locations(ssl_context, certdata):
|
|
118
|
+
"""Load certificate verification locations into SSL context.
|
|
119
|
+
|
|
120
|
+
:param ssl_context: The SSL context to configure
|
|
121
|
+
:type ssl_context: ssl.SSLContext
|
|
122
|
+
:param certdata: Certificate data (file path or PEM string)
|
|
123
|
+
:type certdata: str or None
|
|
124
|
+
"""
|
|
100
125
|
if certdata is None:
|
|
101
126
|
return
|
|
102
127
|
|
|
@@ -106,9 +131,17 @@ def ssl_verify_locations(ssl_context, certdata):
|
|
|
106
131
|
ssl_context.load_verify_locations(cafile=certdata)
|
|
107
132
|
|
|
108
133
|
|
|
109
|
-
# As cert must be written in files to be loaded in ssl context
|
|
110
|
-
# Creates a temporary file in the most secure manner possible
|
|
111
134
|
def data_to_temp_file(data):
|
|
135
|
+
"""Write data to a temporary file securely.
|
|
136
|
+
|
|
137
|
+
Creates a temporary file in the most secure manner possible.
|
|
138
|
+
The file is readable and writable only by the creating user ID.
|
|
139
|
+
|
|
140
|
+
:param data: The data to write to the temporary file
|
|
141
|
+
:type data: str
|
|
142
|
+
:return: Path to the created temporary file
|
|
143
|
+
:rtype: str
|
|
144
|
+
"""
|
|
112
145
|
# The file is readable and writable only by the creating user ID.
|
|
113
146
|
# If the operating system uses permission bits to indicate whether a
|
|
114
147
|
# file is executable, the file is executable by no one. The file
|
|
@@ -121,6 +154,17 @@ def data_to_temp_file(data):
|
|
|
121
154
|
|
|
122
155
|
|
|
123
156
|
def ssl_cert_chain(ssl_context, cert_data, key_data, passphrase):
|
|
157
|
+
"""Load certificate chain into SSL context.
|
|
158
|
+
|
|
159
|
+
:param ssl_context: The SSL context to configure
|
|
160
|
+
:type ssl_context: ssl.SSLContext
|
|
161
|
+
:param cert_data: Certificate data (file path or PEM string)
|
|
162
|
+
:type cert_data: str or None
|
|
163
|
+
:param key_data: Private key data (file path or PEM string)
|
|
164
|
+
:type key_data: str or None
|
|
165
|
+
:param passphrase: Passphrase for the private key
|
|
166
|
+
:type passphrase: str or None
|
|
167
|
+
"""
|
|
124
168
|
if cert_data is None:
|
|
125
169
|
return
|
|
126
170
|
|
|
@@ -147,6 +191,13 @@ def ssl_cert_chain(ssl_context, cert_data, key_data, passphrase):
|
|
|
147
191
|
|
|
148
192
|
|
|
149
193
|
def create_callback_ssl_context(config) -> ssl.SSLContext:
|
|
194
|
+
"""Create SSL context for API callback server.
|
|
195
|
+
|
|
196
|
+
:param config: Configuration dictionary
|
|
197
|
+
:type config: dict
|
|
198
|
+
:return: Configured SSL context
|
|
199
|
+
:rtype: ssl.SSLContext
|
|
200
|
+
"""
|
|
150
201
|
listen_protocol_api_ssl_key = get_config_variable(
|
|
151
202
|
"LISTEN_PROTOCOL_API_SSL_KEY",
|
|
152
203
|
["connector", "listen_protocol_api_ssl_key"],
|
|
@@ -176,6 +227,13 @@ def create_callback_ssl_context(config) -> ssl.SSLContext:
|
|
|
176
227
|
|
|
177
228
|
|
|
178
229
|
def create_mq_ssl_context(config) -> ssl.SSLContext:
|
|
230
|
+
"""Create SSL context for message queue connections.
|
|
231
|
+
|
|
232
|
+
:param config: Configuration dictionary
|
|
233
|
+
:type config: dict
|
|
234
|
+
:return: Configured SSL context for MQ connections
|
|
235
|
+
:rtype: ssl.SSLContext
|
|
236
|
+
"""
|
|
179
237
|
use_ssl_ca = get_config_variable("MQ_USE_SSL_CA", ["mq", "use_ssl_ca"], config)
|
|
180
238
|
use_ssl_cert = get_config_variable(
|
|
181
239
|
"MQ_USE_SSL_CERT", ["mq", "use_ssl_cert"], config
|
|
@@ -292,6 +350,11 @@ class ListenQueue(threading.Thread):
|
|
|
292
350
|
)
|
|
293
351
|
|
|
294
352
|
def _set_draft_id(self, draft_id):
|
|
353
|
+
"""Set the draft ID for the helper and API instances.
|
|
354
|
+
|
|
355
|
+
:param draft_id: The draft ID to set
|
|
356
|
+
:type draft_id: str
|
|
357
|
+
"""
|
|
295
358
|
self.helper.draft_id = draft_id
|
|
296
359
|
self.helper.api.set_draft_id(draft_id)
|
|
297
360
|
self.helper.api_impersonate.set_draft_id(draft_id)
|
|
@@ -546,6 +609,11 @@ class ListenQueue(threading.Thread):
|
|
|
546
609
|
raise ValueError("Unsupported listen protocol type")
|
|
547
610
|
|
|
548
611
|
def stop(self):
|
|
612
|
+
"""Stop the ListenQueue thread and close connections.
|
|
613
|
+
|
|
614
|
+
This method sets the exit event, closes the RabbitMQ connection,
|
|
615
|
+
and waits for the processing thread to complete.
|
|
616
|
+
"""
|
|
549
617
|
self.helper.connector_logger.info("Preparing ListenQueue for clean shutdown")
|
|
550
618
|
self.exit_event.set()
|
|
551
619
|
self.pika_connection.close()
|
|
@@ -794,6 +862,10 @@ class ListenStream(threading.Thread):
|
|
|
794
862
|
sys.excepthook(*sys.exc_info())
|
|
795
863
|
|
|
796
864
|
def stop(self):
|
|
865
|
+
"""Stop the ListenStream thread.
|
|
866
|
+
|
|
867
|
+
This method sets the exit event to signal the stream listening thread to stop.
|
|
868
|
+
"""
|
|
797
869
|
self.helper.connector_logger.info("Preparing ListenStream for clean shutdown")
|
|
798
870
|
self.exit_event.set()
|
|
799
871
|
|
|
@@ -817,6 +889,11 @@ class ConnectorInfo:
|
|
|
817
889
|
|
|
818
890
|
@property
|
|
819
891
|
def all_details(self):
|
|
892
|
+
"""Get all connector information details as a dictionary.
|
|
893
|
+
|
|
894
|
+
:return: Dictionary containing all connector status information
|
|
895
|
+
:rtype: dict
|
|
896
|
+
"""
|
|
820
897
|
return {
|
|
821
898
|
"run_and_terminate": self._run_and_terminate,
|
|
822
899
|
"buffering": self._buffering,
|
|
@@ -832,6 +909,11 @@ class ConnectorInfo:
|
|
|
832
909
|
|
|
833
910
|
@run_and_terminate.setter
|
|
834
911
|
def run_and_terminate(self, value):
|
|
912
|
+
"""Set the run_and_terminate flag.
|
|
913
|
+
|
|
914
|
+
:param value: Whether the connector should run once and terminate
|
|
915
|
+
:type value: bool
|
|
916
|
+
"""
|
|
835
917
|
self._run_and_terminate = value
|
|
836
918
|
|
|
837
919
|
@property
|
|
@@ -840,6 +922,11 @@ class ConnectorInfo:
|
|
|
840
922
|
|
|
841
923
|
@buffering.setter
|
|
842
924
|
def buffering(self, value):
|
|
925
|
+
"""Set the buffering status.
|
|
926
|
+
|
|
927
|
+
:param value: Whether the connector is currently buffering
|
|
928
|
+
:type value: bool
|
|
929
|
+
"""
|
|
843
930
|
self._buffering = value
|
|
844
931
|
|
|
845
932
|
@property
|
|
@@ -848,6 +935,11 @@ class ConnectorInfo:
|
|
|
848
935
|
|
|
849
936
|
@queue_threshold.setter
|
|
850
937
|
def queue_threshold(self, value):
|
|
938
|
+
"""Set the queue threshold value.
|
|
939
|
+
|
|
940
|
+
:param value: The queue size threshold in MB
|
|
941
|
+
:type value: float
|
|
942
|
+
"""
|
|
851
943
|
self._queue_threshold = value
|
|
852
944
|
|
|
853
945
|
@property
|
|
@@ -856,6 +948,11 @@ class ConnectorInfo:
|
|
|
856
948
|
|
|
857
949
|
@queue_messages_size.setter
|
|
858
950
|
def queue_messages_size(self, value):
|
|
951
|
+
"""Set the current queue messages size.
|
|
952
|
+
|
|
953
|
+
:param value: The current size of messages in the queue in MB
|
|
954
|
+
:type value: float
|
|
955
|
+
"""
|
|
859
956
|
self._queue_messages_size = value
|
|
860
957
|
|
|
861
958
|
@property
|
|
@@ -864,6 +961,11 @@ class ConnectorInfo:
|
|
|
864
961
|
|
|
865
962
|
@next_run_datetime.setter
|
|
866
963
|
def next_run_datetime(self, value):
|
|
964
|
+
"""Set the next scheduled run datetime.
|
|
965
|
+
|
|
966
|
+
:param value: The datetime for the next scheduled run
|
|
967
|
+
:type value: datetime
|
|
968
|
+
"""
|
|
867
969
|
self._next_run_datetime = value
|
|
868
970
|
|
|
869
971
|
@property
|
|
@@ -872,6 +974,11 @@ class ConnectorInfo:
|
|
|
872
974
|
|
|
873
975
|
@last_run_datetime.setter
|
|
874
976
|
def last_run_datetime(self, value):
|
|
977
|
+
"""Set the last run datetime.
|
|
978
|
+
|
|
979
|
+
:param value: The datetime of the last run
|
|
980
|
+
:type value: datetime
|
|
981
|
+
"""
|
|
875
982
|
self._last_run_datetime = value
|
|
876
983
|
|
|
877
984
|
|
|
@@ -1062,6 +1169,20 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
|
|
|
1062
1169
|
config,
|
|
1063
1170
|
default=False,
|
|
1064
1171
|
)
|
|
1172
|
+
metrics_namespace = get_config_variable(
|
|
1173
|
+
"CONNECTOR_METRICS_NAMESPACE",
|
|
1174
|
+
["connector", "metrics_namespace"],
|
|
1175
|
+
config,
|
|
1176
|
+
False,
|
|
1177
|
+
"",
|
|
1178
|
+
)
|
|
1179
|
+
metrics_subsystem = get_config_variable(
|
|
1180
|
+
"CONNECTOR_METRICS_SUBSYSTEM",
|
|
1181
|
+
["connector", "metrics_subsystem"],
|
|
1182
|
+
config,
|
|
1183
|
+
False,
|
|
1184
|
+
"",
|
|
1185
|
+
)
|
|
1065
1186
|
metrics_port = get_config_variable(
|
|
1066
1187
|
"CONNECTOR_METRICS_PORT",
|
|
1067
1188
|
["connector", "metrics_port"],
|
|
@@ -1102,7 +1223,11 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
|
|
|
1102
1223
|
# For retro compatibility
|
|
1103
1224
|
|
|
1104
1225
|
self.metric = OpenCTIMetricHandler(
|
|
1105
|
-
self.connector_logger,
|
|
1226
|
+
self.connector_logger,
|
|
1227
|
+
expose_metrics,
|
|
1228
|
+
metrics_namespace,
|
|
1229
|
+
metrics_subsystem,
|
|
1230
|
+
metrics_port,
|
|
1106
1231
|
)
|
|
1107
1232
|
# Register the connector in OpenCTI
|
|
1108
1233
|
self.connector = OpenCTIConnector(
|
|
@@ -1226,6 +1351,11 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
|
|
|
1226
1351
|
self.listen_queue = None
|
|
1227
1352
|
|
|
1228
1353
|
def stop(self) -> None:
|
|
1354
|
+
"""Stop the connector and clean up resources.
|
|
1355
|
+
|
|
1356
|
+
This method stops all running threads (listen queue, ping thread) and
|
|
1357
|
+
unregisters the connector from OpenCTI.
|
|
1358
|
+
"""
|
|
1229
1359
|
self.connector_logger.info("Preparing connector for clean shutdown")
|
|
1230
1360
|
if self.listen_queue:
|
|
1231
1361
|
self.listen_queue.stop()
|
|
@@ -1235,9 +1365,20 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
|
|
|
1235
1365
|
self.api.connector.unregister(self.connector_id)
|
|
1236
1366
|
|
|
1237
1367
|
def get_name(self) -> Optional[Union[bool, int, str]]:
|
|
1368
|
+
"""Get the connector name.
|
|
1369
|
+
|
|
1370
|
+
:return: The name of the connector
|
|
1371
|
+
:rtype: Optional[Union[bool, int, str]]
|
|
1372
|
+
"""
|
|
1238
1373
|
return self.connect_name
|
|
1239
1374
|
|
|
1240
1375
|
def get_stream_collection(self):
|
|
1376
|
+
"""Get the stream collection configuration.
|
|
1377
|
+
|
|
1378
|
+
:return: Stream collection configuration dictionary
|
|
1379
|
+
:rtype: dict
|
|
1380
|
+
:raises ValueError: If no stream is connected
|
|
1381
|
+
"""
|
|
1241
1382
|
if self.connect_live_stream_id is not None:
|
|
1242
1383
|
if self.connect_live_stream_id in ["live", "raw"]:
|
|
1243
1384
|
return {
|
|
@@ -1272,12 +1413,27 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
|
|
|
1272
1413
|
raise ValueError("This connector is not connected to any stream")
|
|
1273
1414
|
|
|
1274
1415
|
def get_only_contextual(self) -> Optional[Union[bool, int, str]]:
|
|
1416
|
+
"""Get the only_contextual configuration value.
|
|
1417
|
+
|
|
1418
|
+
:return: Whether the connector processes only contextual data
|
|
1419
|
+
:rtype: Optional[Union[bool, int, str]]
|
|
1420
|
+
"""
|
|
1275
1421
|
return self.connect_only_contextual
|
|
1276
1422
|
|
|
1277
1423
|
def get_run_and_terminate(self) -> Optional[Union[bool, int, str]]:
|
|
1424
|
+
"""Get the run_and_terminate configuration value.
|
|
1425
|
+
|
|
1426
|
+
:return: Whether the connector should run once and terminate
|
|
1427
|
+
:rtype: Optional[Union[bool, int, str]]
|
|
1428
|
+
"""
|
|
1278
1429
|
return self.connect_run_and_terminate
|
|
1279
1430
|
|
|
1280
1431
|
def get_validate_before_import(self) -> Optional[Union[bool, int, str]]:
|
|
1432
|
+
"""Get the validate_before_import configuration value.
|
|
1433
|
+
|
|
1434
|
+
:return: Whether to validate data before importing
|
|
1435
|
+
:rtype: Optional[Union[bool, int, str]]
|
|
1436
|
+
"""
|
|
1281
1437
|
return self.connect_validate_before_import
|
|
1282
1438
|
|
|
1283
1439
|
def set_state(self, state) -> None:
|
|
@@ -1308,6 +1464,11 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
|
|
|
1308
1464
|
return None
|
|
1309
1465
|
|
|
1310
1466
|
def force_ping(self):
|
|
1467
|
+
"""Force a ping to the OpenCTI API to update connector state.
|
|
1468
|
+
|
|
1469
|
+
This method manually triggers a ping to synchronize the connector state
|
|
1470
|
+
with the OpenCTI platform.
|
|
1471
|
+
"""
|
|
1311
1472
|
try:
|
|
1312
1473
|
initial_state = self.get_state()
|
|
1313
1474
|
connector_info = self.connector_info.all_details
|
|
@@ -1720,12 +1881,27 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
|
|
|
1720
1881
|
return self.listen_stream
|
|
1721
1882
|
|
|
1722
1883
|
def get_opencti_url(self) -> Optional[Union[bool, int, str]]:
|
|
1884
|
+
"""Get the OpenCTI URL.
|
|
1885
|
+
|
|
1886
|
+
:return: The URL of the OpenCTI platform
|
|
1887
|
+
:rtype: Optional[Union[bool, int, str]]
|
|
1888
|
+
"""
|
|
1723
1889
|
return self.opencti_url
|
|
1724
1890
|
|
|
1725
1891
|
def get_opencti_token(self) -> Optional[Union[bool, int, str]]:
|
|
1892
|
+
"""Get the OpenCTI API token.
|
|
1893
|
+
|
|
1894
|
+
:return: The API token for OpenCTI authentication
|
|
1895
|
+
:rtype: Optional[Union[bool, int, str]]
|
|
1896
|
+
"""
|
|
1726
1897
|
return self.opencti_token
|
|
1727
1898
|
|
|
1728
1899
|
def get_connector(self) -> OpenCTIConnector:
|
|
1900
|
+
"""Get the OpenCTIConnector instance.
|
|
1901
|
+
|
|
1902
|
+
:return: The OpenCTIConnector instance
|
|
1903
|
+
:rtype: OpenCTIConnector
|
|
1904
|
+
"""
|
|
1729
1905
|
return self.connector
|
|
1730
1906
|
|
|
1731
1907
|
def date_now(self) -> str:
|
|
@@ -2275,6 +2451,17 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
|
|
|
2275
2451
|
return None
|
|
2276
2452
|
|
|
2277
2453
|
def get_data_from_enrichment(self, data, standard_id, opencti_entity):
|
|
2454
|
+
"""Extract STIX entity and objects from enrichment data.
|
|
2455
|
+
|
|
2456
|
+
:param data: The enrichment data containing a bundle
|
|
2457
|
+
:type data: dict
|
|
2458
|
+
:param standard_id: The STIX standard ID of the entity
|
|
2459
|
+
:type standard_id: str
|
|
2460
|
+
:param opencti_entity: The OpenCTI entity object
|
|
2461
|
+
:type opencti_entity: dict
|
|
2462
|
+
:return: Dictionary containing stix_entity and stix_objects
|
|
2463
|
+
:rtype: dict
|
|
2464
|
+
"""
|
|
2278
2465
|
bundle = data.get("bundle", None)
|
|
2279
2466
|
# Extract main entity from bundle in case of playbook
|
|
2280
2467
|
if bundle is None:
|
|
@@ -4,7 +4,14 @@ from prometheus_client import Counter, Enum, start_http_server
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class OpenCTIMetricHandler:
|
|
7
|
-
def __init__(
|
|
7
|
+
def __init__(
|
|
8
|
+
self,
|
|
9
|
+
connector_logger,
|
|
10
|
+
activated: bool = False,
|
|
11
|
+
namespace: str = "",
|
|
12
|
+
subsystem: str = "",
|
|
13
|
+
port: int = 9095,
|
|
14
|
+
):
|
|
8
15
|
"""
|
|
9
16
|
Init of OpenCTIMetricHandler class.
|
|
10
17
|
|
|
@@ -12,6 +19,10 @@ class OpenCTIMetricHandler:
|
|
|
12
19
|
----------
|
|
13
20
|
activated : bool, default False
|
|
14
21
|
If True use metrics in client and connectors.
|
|
22
|
+
namespace: str, default empty
|
|
23
|
+
Namespace for the prometheus metrics.
|
|
24
|
+
subsystem: str, default empty
|
|
25
|
+
Subsystem for the prometheus metrics.
|
|
15
26
|
port : int, default 9095
|
|
16
27
|
Port for prometheus server.
|
|
17
28
|
"""
|
|
@@ -22,35 +33,53 @@ class OpenCTIMetricHandler:
|
|
|
22
33
|
start_http_server(port)
|
|
23
34
|
self._metrics = {
|
|
24
35
|
"bundle_send": Counter(
|
|
25
|
-
"
|
|
26
|
-
"Number of
|
|
36
|
+
"bundles_sent_total",
|
|
37
|
+
"Number of bundles sent",
|
|
38
|
+
namespace=namespace,
|
|
39
|
+
subsystem=subsystem,
|
|
27
40
|
),
|
|
28
41
|
"record_send": Counter(
|
|
29
|
-
"
|
|
30
|
-
"Number of
|
|
42
|
+
"records_sent_total",
|
|
43
|
+
"Number of records (objects per bundle) sent",
|
|
44
|
+
namespace=namespace,
|
|
45
|
+
subsystem=subsystem,
|
|
31
46
|
),
|
|
32
47
|
"run_count": Counter(
|
|
33
|
-
"
|
|
48
|
+
"runs_total",
|
|
34
49
|
"Number of run",
|
|
50
|
+
namespace=namespace,
|
|
51
|
+
subsystem=subsystem,
|
|
35
52
|
),
|
|
36
53
|
"ping_api_count": Counter(
|
|
37
|
-
"
|
|
38
|
-
"Number of
|
|
54
|
+
"ping_api_total",
|
|
55
|
+
"Number of pings to the API",
|
|
56
|
+
namespace=namespace,
|
|
57
|
+
subsystem=subsystem,
|
|
39
58
|
),
|
|
40
59
|
"ping_api_error": Counter(
|
|
41
|
-
"
|
|
42
|
-
"Number of
|
|
60
|
+
"ping_api_errors_total",
|
|
61
|
+
"Number of errors when pinging the API",
|
|
62
|
+
namespace=namespace,
|
|
63
|
+
subsystem=subsystem,
|
|
43
64
|
),
|
|
44
65
|
"error_count": Counter(
|
|
45
|
-
"
|
|
46
|
-
"Number of
|
|
66
|
+
"errors_total",
|
|
67
|
+
"Number of errors",
|
|
68
|
+
namespace=namespace,
|
|
69
|
+
subsystem=subsystem,
|
|
47
70
|
),
|
|
48
71
|
"client_error_count": Counter(
|
|
49
|
-
"
|
|
50
|
-
"Number of client
|
|
72
|
+
"client_errors_total",
|
|
73
|
+
"Number of client errors",
|
|
74
|
+
namespace=namespace,
|
|
75
|
+
subsystem=subsystem,
|
|
51
76
|
),
|
|
52
77
|
"state": Enum(
|
|
53
|
-
"state",
|
|
78
|
+
"state",
|
|
79
|
+
"State of connector",
|
|
80
|
+
states=["idle", "running", "stopped"],
|
|
81
|
+
namespace=namespace,
|
|
82
|
+
subsystem=subsystem,
|
|
54
83
|
),
|
|
55
84
|
}
|
|
56
85
|
|
|
@@ -7,6 +7,11 @@ from stix2.canonicalization.Canonicalize import canonicalize
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class AttackPattern:
|
|
10
|
+
"""Main AttackPattern class for OpenCTI
|
|
11
|
+
|
|
12
|
+
:param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient`
|
|
13
|
+
"""
|
|
14
|
+
|
|
10
15
|
def __init__(self, opencti):
|
|
11
16
|
self.opencti = opencti
|
|
12
17
|
self.properties = """
|
|
@@ -238,6 +243,15 @@ class AttackPattern:
|
|
|
238
243
|
|
|
239
244
|
@staticmethod
|
|
240
245
|
def generate_id(name, x_mitre_id=None):
|
|
246
|
+
"""Generate a STIX ID for an Attack Pattern.
|
|
247
|
+
|
|
248
|
+
:param name: The name of the attack pattern
|
|
249
|
+
:type name: str
|
|
250
|
+
:param x_mitre_id: Optional MITRE ATT&CK ID
|
|
251
|
+
:type x_mitre_id: str or None
|
|
252
|
+
:return: STIX ID for the attack pattern
|
|
253
|
+
:rtype: str
|
|
254
|
+
"""
|
|
241
255
|
if x_mitre_id is not None:
|
|
242
256
|
data = {"x_mitre_id": x_mitre_id.strip()}
|
|
243
257
|
else:
|
|
@@ -248,20 +262,32 @@ class AttackPattern:
|
|
|
248
262
|
|
|
249
263
|
@staticmethod
|
|
250
264
|
def generate_id_from_data(data):
|
|
265
|
+
"""Generate a STIX ID from attack pattern data.
|
|
266
|
+
|
|
267
|
+
:param data: Dictionary containing 'name' and optionally 'x_mitre_id' keys
|
|
268
|
+
:type data: dict
|
|
269
|
+
:return: STIX ID for the attack pattern
|
|
270
|
+
:rtype: str
|
|
271
|
+
"""
|
|
251
272
|
external_id = data.get("x_mitre_id") or data.get("x_opencti_external_id")
|
|
252
273
|
return AttackPattern.generate_id(data.get("name"), external_id)
|
|
253
274
|
|
|
254
|
-
|
|
255
|
-
List Attack
|
|
275
|
+
def list(self, **kwargs):
|
|
276
|
+
"""List Attack Pattern objects.
|
|
256
277
|
|
|
257
278
|
:param filters: the filters to apply
|
|
258
279
|
:param search: the search keyword
|
|
259
280
|
:param first: return the first n rows from the after ID (or the beginning if not set)
|
|
260
281
|
:param after: ID of the first row for pagination
|
|
261
|
-
:
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
282
|
+
:param orderBy: field to order results by
|
|
283
|
+
:param orderMode: ordering mode (asc/desc)
|
|
284
|
+
:param customAttributes: custom attributes to return
|
|
285
|
+
:param getAll: whether to retrieve all results
|
|
286
|
+
:param withPagination: whether to include pagination info
|
|
287
|
+
:param withFiles: whether to include files
|
|
288
|
+
:return: List of Attack Pattern objects
|
|
289
|
+
:rtype: list
|
|
290
|
+
"""
|
|
265
291
|
filters = kwargs.get("filters", None)
|
|
266
292
|
search = kwargs.get("search", None)
|
|
267
293
|
first = kwargs.get("first", 500)
|
|
@@ -7,6 +7,11 @@ from stix2.canonicalization.Canonicalize import canonicalize
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class Campaign:
|
|
10
|
+
"""Main Campaign class for OpenCTI
|
|
11
|
+
|
|
12
|
+
:param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient`
|
|
13
|
+
"""
|
|
14
|
+
|
|
10
15
|
def __init__(self, opencti):
|
|
11
16
|
self.opencti = opencti
|
|
12
17
|
self.properties = """
|
|
@@ -226,6 +231,13 @@ class Campaign:
|
|
|
226
231
|
|
|
227
232
|
@staticmethod
|
|
228
233
|
def generate_id(name):
|
|
234
|
+
"""Generate a STIX ID for a Campaign.
|
|
235
|
+
|
|
236
|
+
:param name: The name of the campaign
|
|
237
|
+
:type name: str
|
|
238
|
+
:return: STIX ID for the campaign
|
|
239
|
+
:rtype: str
|
|
240
|
+
"""
|
|
229
241
|
name = name.lower().strip()
|
|
230
242
|
data = {"name": name}
|
|
231
243
|
data = canonicalize(data, utf8=False)
|
|
@@ -234,19 +246,31 @@ class Campaign:
|
|
|
234
246
|
|
|
235
247
|
@staticmethod
|
|
236
248
|
def generate_id_from_data(data):
|
|
249
|
+
"""Generate a STIX ID from campaign data.
|
|
250
|
+
|
|
251
|
+
:param data: Dictionary containing 'name' key
|
|
252
|
+
:type data: dict
|
|
253
|
+
:return: STIX ID for the campaign
|
|
254
|
+
:rtype: str
|
|
255
|
+
"""
|
|
237
256
|
return Campaign.generate_id(data["name"])
|
|
238
257
|
|
|
239
|
-
|
|
240
|
-
List Campaign objects
|
|
258
|
+
def list(self, **kwargs):
|
|
259
|
+
"""List Campaign objects.
|
|
241
260
|
|
|
242
261
|
:param filters: the filters to apply
|
|
243
262
|
:param search: the search keyword
|
|
244
263
|
:param first: return the first n rows from the after ID (or the beginning if not set)
|
|
245
264
|
:param after: ID of the first row for pagination
|
|
246
|
-
:
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
265
|
+
:param orderBy: field to order results by
|
|
266
|
+
:param orderMode: ordering mode (asc/desc)
|
|
267
|
+
:param customAttributes: custom attributes to return
|
|
268
|
+
:param getAll: whether to retrieve all results
|
|
269
|
+
:param withPagination: whether to include pagination info
|
|
270
|
+
:param withFiles: whether to include files
|
|
271
|
+
:return: List of Campaign objects
|
|
272
|
+
:rtype: list
|
|
273
|
+
"""
|
|
250
274
|
filters = kwargs.get("filters", None)
|
|
251
275
|
search = kwargs.get("search", None)
|
|
252
276
|
first = kwargs.get("first", 500)
|
|
@@ -7,6 +7,13 @@ from stix2.canonicalization.Canonicalize import canonicalize
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class CaseIncident:
|
|
10
|
+
"""Main CaseIncident class for OpenCTI
|
|
11
|
+
|
|
12
|
+
Manages incident response cases in the OpenCTI platform.
|
|
13
|
+
|
|
14
|
+
:param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient`
|
|
15
|
+
"""
|
|
16
|
+
|
|
10
17
|
def __init__(self, opencti):
|
|
11
18
|
self.opencti = opencti
|
|
12
19
|
self.properties = """
|
|
@@ -7,6 +7,13 @@ from stix2.canonicalization.Canonicalize import canonicalize
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class CaseRfi:
|
|
10
|
+
"""Main CaseRfi (Request for Information) class for OpenCTI
|
|
11
|
+
|
|
12
|
+
Manages RFI cases in the OpenCTI platform.
|
|
13
|
+
|
|
14
|
+
:param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient`
|
|
15
|
+
"""
|
|
16
|
+
|
|
10
17
|
def __init__(self, opencti):
|
|
11
18
|
self.opencti = opencti
|
|
12
19
|
self.properties = """
|
|
@@ -7,6 +7,13 @@ from stix2.canonicalization.Canonicalize import canonicalize
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class CaseRft:
|
|
10
|
+
"""Main CaseRft (Request for Takedown) class for OpenCTI
|
|
11
|
+
|
|
12
|
+
Manages RFT cases in the OpenCTI platform.
|
|
13
|
+
|
|
14
|
+
:param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient`
|
|
15
|
+
"""
|
|
16
|
+
|
|
10
17
|
def __init__(self, opencti):
|
|
11
18
|
self.opencti = opencti
|
|
12
19
|
self.properties = """
|
|
@@ -7,6 +7,13 @@ from stix2.canonicalization.Canonicalize import canonicalize
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class Channel:
|
|
10
|
+
"""Main Channel class for OpenCTI
|
|
11
|
+
|
|
12
|
+
Manages communication channels used by threat actors in the OpenCTI platform.
|
|
13
|
+
|
|
14
|
+
:param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient`
|
|
15
|
+
"""
|
|
16
|
+
|
|
10
17
|
def __init__(self, opencti):
|
|
11
18
|
self.opencti = opencti
|
|
12
19
|
self.properties = """
|
|
@@ -7,6 +7,13 @@ from stix2.canonicalization.Canonicalize import canonicalize
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class CourseOfAction:
|
|
10
|
+
"""Main CourseOfAction class for OpenCTI
|
|
11
|
+
|
|
12
|
+
Manages courses of action (mitigations) in the OpenCTI platform.
|
|
13
|
+
|
|
14
|
+
:param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient`
|
|
15
|
+
"""
|
|
16
|
+
|
|
10
17
|
def __init__(self, opencti):
|
|
11
18
|
self.opencti = opencti
|
|
12
19
|
self.properties = """
|