pycti 6.2.10__py3-none-any.whl → 6.2.12__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/api/opencti_api_connector.py +58 -4
- pycti/connector/opencti_connector_helper.py +433 -14
- pycti/entities/opencti_stix_core_object.py +6 -0
- pycti/entities/opencti_stix_domain_object.py +6 -0
- pycti/entities/opencti_stix_object_or_stix_relationship.py +3 -0
- pycti/entities/opencti_vulnerability.py +26 -0
- {pycti-6.2.10.dist-info → pycti-6.2.12.dist-info}/METADATA +31 -30
- {pycti-6.2.10.dist-info → pycti-6.2.12.dist-info}/RECORD +12 -12
- {pycti-6.2.10.dist-info → pycti-6.2.12.dist-info}/LICENSE +0 -0
- {pycti-6.2.10.dist-info → pycti-6.2.12.dist-info}/WHEEL +0 -0
- {pycti-6.2.10.dist-info → pycti-6.2.12.dist-info}/top_level.txt +0 -0
pycti/__init__.py
CHANGED
|
@@ -10,6 +10,43 @@ class OpenCTIApiConnector:
|
|
|
10
10
|
def __init__(self, api):
|
|
11
11
|
self.api = api
|
|
12
12
|
|
|
13
|
+
def read(self, connector_id: str) -> Dict:
|
|
14
|
+
"""Reading the connector and its details
|
|
15
|
+
|
|
16
|
+
:return: return all the connector details
|
|
17
|
+
:rtype: dict
|
|
18
|
+
"""
|
|
19
|
+
self.api.app_logger.info("[INFO] Getting connector details ...")
|
|
20
|
+
query = """
|
|
21
|
+
query GetConnector($id: String!) {
|
|
22
|
+
connector(id: $id) {
|
|
23
|
+
id
|
|
24
|
+
name
|
|
25
|
+
active
|
|
26
|
+
auto
|
|
27
|
+
only_contextual
|
|
28
|
+
connector_type
|
|
29
|
+
connector_scope
|
|
30
|
+
connector_state
|
|
31
|
+
connector_queue_details {
|
|
32
|
+
messages_number
|
|
33
|
+
messages_size
|
|
34
|
+
}
|
|
35
|
+
updated_at
|
|
36
|
+
created_at
|
|
37
|
+
config {
|
|
38
|
+
listen
|
|
39
|
+
listen_exchange
|
|
40
|
+
push
|
|
41
|
+
push_exchange
|
|
42
|
+
}
|
|
43
|
+
built_in
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
"""
|
|
47
|
+
result = self.api.query(query, {"id": connector_id})
|
|
48
|
+
return result["data"]["connector"]
|
|
49
|
+
|
|
13
50
|
def list(self) -> Dict:
|
|
14
51
|
"""list available connectors
|
|
15
52
|
|
|
@@ -41,27 +78,44 @@ class OpenCTIApiConnector:
|
|
|
41
78
|
result = self.api.query(query)
|
|
42
79
|
return result["data"]["connectorsForWorker"]
|
|
43
80
|
|
|
44
|
-
def ping(
|
|
81
|
+
def ping(
|
|
82
|
+
self, connector_id: str, connector_state: Any, connector_info: Dict
|
|
83
|
+
) -> Dict:
|
|
45
84
|
"""pings a connector by id and state
|
|
46
85
|
|
|
47
86
|
:param connector_id: the connectors id
|
|
48
87
|
:type connector_id: str
|
|
49
88
|
:param connector_state: state for the connector
|
|
50
89
|
:type connector_state:
|
|
90
|
+
:param connector_info: all details connector
|
|
91
|
+
:type connector_info: Dict
|
|
51
92
|
:return: the response pingConnector data dict
|
|
52
93
|
:rtype: dict
|
|
53
94
|
"""
|
|
54
95
|
|
|
55
96
|
query = """
|
|
56
|
-
mutation PingConnector($id: ID!, $state: String) {
|
|
57
|
-
pingConnector(id: $id, state: $state) {
|
|
97
|
+
mutation PingConnector($id: ID!, $state: String, $connectorInfo: ConnectorInfoInput ) {
|
|
98
|
+
pingConnector(id: $id, state: $state, connectorInfo: $connectorInfo) {
|
|
58
99
|
id
|
|
59
100
|
connector_state
|
|
101
|
+
connector_info {
|
|
102
|
+
run_and_terminate
|
|
103
|
+
buffering
|
|
104
|
+
queue_threshold
|
|
105
|
+
queue_messages_size
|
|
106
|
+
next_run_datetime
|
|
107
|
+
last_run_datetime
|
|
108
|
+
}
|
|
60
109
|
}
|
|
61
110
|
}
|
|
62
111
|
"""
|
|
63
112
|
result = self.api.query(
|
|
64
|
-
query,
|
|
113
|
+
query,
|
|
114
|
+
{
|
|
115
|
+
"id": connector_id,
|
|
116
|
+
"state": json.dumps(connector_state),
|
|
117
|
+
"connectorInfo": connector_info,
|
|
118
|
+
},
|
|
65
119
|
)
|
|
66
120
|
return result["data"]["pingConnector"]
|
|
67
121
|
|
|
@@ -5,6 +5,7 @@ import datetime
|
|
|
5
5
|
import json
|
|
6
6
|
import os
|
|
7
7
|
import queue
|
|
8
|
+
import sched
|
|
8
9
|
import signal
|
|
9
10
|
import ssl
|
|
10
11
|
import sys
|
|
@@ -12,12 +13,14 @@ import tempfile
|
|
|
12
13
|
import threading
|
|
13
14
|
import time
|
|
14
15
|
import uuid
|
|
16
|
+
from enum import Enum
|
|
15
17
|
from queue import Queue
|
|
16
18
|
from typing import Callable, Dict, List, Optional, Union
|
|
17
19
|
|
|
18
20
|
import pika
|
|
19
21
|
from filigran_sseclient import SSEClient
|
|
20
22
|
from pika.exceptions import NackError, UnroutableError
|
|
23
|
+
from pydantic import TypeAdapter
|
|
21
24
|
|
|
22
25
|
from pycti.api.opencti_api_client import OpenCTIApiClient
|
|
23
26
|
from pycti.connector.opencti_connector import OpenCTIConnector
|
|
@@ -419,7 +422,14 @@ class ListenQueue(threading.Thread):
|
|
|
419
422
|
|
|
420
423
|
class PingAlive(threading.Thread):
|
|
421
424
|
def __init__(
|
|
422
|
-
self,
|
|
425
|
+
self,
|
|
426
|
+
connector_logger,
|
|
427
|
+
connector_id,
|
|
428
|
+
api,
|
|
429
|
+
get_state,
|
|
430
|
+
set_state,
|
|
431
|
+
metric,
|
|
432
|
+
connector_info,
|
|
423
433
|
) -> None:
|
|
424
434
|
threading.Thread.__init__(self, daemon=True)
|
|
425
435
|
self.connector_logger = connector_logger
|
|
@@ -430,13 +440,20 @@ class PingAlive(threading.Thread):
|
|
|
430
440
|
self.set_state = set_state
|
|
431
441
|
self.exit_event = threading.Event()
|
|
432
442
|
self.metric = metric
|
|
443
|
+
self.connector_info = connector_info
|
|
433
444
|
|
|
434
445
|
def ping(self) -> None:
|
|
435
446
|
while not self.exit_event.is_set():
|
|
436
447
|
try:
|
|
437
448
|
self.connector_logger.debug("PingAlive running.")
|
|
438
449
|
initial_state = self.get_state()
|
|
439
|
-
|
|
450
|
+
connector_info = self.connector_info.all_details
|
|
451
|
+
self.connector_logger.debug(
|
|
452
|
+
"PingAlive ConnectorInfo", {"connector_info": connector_info}
|
|
453
|
+
)
|
|
454
|
+
result = self.api.connector.ping(
|
|
455
|
+
self.connector_id, initial_state, connector_info
|
|
456
|
+
)
|
|
440
457
|
remote_state = (
|
|
441
458
|
json.loads(result["connector_state"])
|
|
442
459
|
if result["connector_state"] is not None
|
|
@@ -646,6 +663,83 @@ class ListenStream(threading.Thread):
|
|
|
646
663
|
self.exit_event.set()
|
|
647
664
|
|
|
648
665
|
|
|
666
|
+
class ConnectorInfo:
|
|
667
|
+
def __init__(
|
|
668
|
+
self,
|
|
669
|
+
run_and_terminate: bool = False,
|
|
670
|
+
buffering: bool = False,
|
|
671
|
+
queue_threshold: float = 500,
|
|
672
|
+
queue_messages_size: float = 0,
|
|
673
|
+
next_run_datetime: datetime = None,
|
|
674
|
+
last_run_datetime: datetime = None,
|
|
675
|
+
):
|
|
676
|
+
self._run_and_terminate = run_and_terminate
|
|
677
|
+
self._buffering = buffering
|
|
678
|
+
self._queue_threshold = queue_threshold
|
|
679
|
+
self._queue_messages_size = queue_messages_size
|
|
680
|
+
self._next_run_datetime = next_run_datetime
|
|
681
|
+
self._last_run_datetime = last_run_datetime
|
|
682
|
+
|
|
683
|
+
@property
|
|
684
|
+
def all_details(self):
|
|
685
|
+
return {
|
|
686
|
+
"run_and_terminate": self._run_and_terminate,
|
|
687
|
+
"buffering": self._buffering,
|
|
688
|
+
"queue_threshold": self._queue_threshold,
|
|
689
|
+
"queue_messages_size": self._queue_messages_size,
|
|
690
|
+
"next_run_datetime": self._next_run_datetime,
|
|
691
|
+
"last_run_datetime": self._last_run_datetime,
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
@property
|
|
695
|
+
def run_and_terminate(self) -> bool:
|
|
696
|
+
return self._run_and_terminate
|
|
697
|
+
|
|
698
|
+
@run_and_terminate.setter
|
|
699
|
+
def run_and_terminate(self, value):
|
|
700
|
+
self._run_and_terminate = value
|
|
701
|
+
|
|
702
|
+
@property
|
|
703
|
+
def buffering(self) -> bool:
|
|
704
|
+
return self._buffering
|
|
705
|
+
|
|
706
|
+
@buffering.setter
|
|
707
|
+
def buffering(self, value):
|
|
708
|
+
self._buffering = value
|
|
709
|
+
|
|
710
|
+
@property
|
|
711
|
+
def queue_threshold(self) -> float:
|
|
712
|
+
return self._queue_threshold
|
|
713
|
+
|
|
714
|
+
@queue_threshold.setter
|
|
715
|
+
def queue_threshold(self, value):
|
|
716
|
+
self._queue_threshold = value
|
|
717
|
+
|
|
718
|
+
@property
|
|
719
|
+
def queue_messages_size(self) -> float:
|
|
720
|
+
return self._queue_messages_size
|
|
721
|
+
|
|
722
|
+
@queue_messages_size.setter
|
|
723
|
+
def queue_messages_size(self, value):
|
|
724
|
+
self._queue_messages_size = value
|
|
725
|
+
|
|
726
|
+
@property
|
|
727
|
+
def next_run_datetime(self) -> datetime:
|
|
728
|
+
return self._next_run_datetime
|
|
729
|
+
|
|
730
|
+
@next_run_datetime.setter
|
|
731
|
+
def next_run_datetime(self, value):
|
|
732
|
+
self._next_run_datetime = value
|
|
733
|
+
|
|
734
|
+
@property
|
|
735
|
+
def last_run_datetime(self) -> datetime:
|
|
736
|
+
return self._last_run_datetime
|
|
737
|
+
|
|
738
|
+
@last_run_datetime.setter
|
|
739
|
+
def last_run_datetime(self, value):
|
|
740
|
+
self._last_run_datetime = value
|
|
741
|
+
|
|
742
|
+
|
|
649
743
|
class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
|
|
650
744
|
"""Python API for OpenCTI connector
|
|
651
745
|
|
|
@@ -653,6 +747,14 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
|
|
|
653
747
|
:type config: Dict
|
|
654
748
|
"""
|
|
655
749
|
|
|
750
|
+
class TimeUnit(Enum):
|
|
751
|
+
SECONDS = 1
|
|
752
|
+
MINUTES = 60
|
|
753
|
+
HOURS = 3600
|
|
754
|
+
DAYS = 86400
|
|
755
|
+
WEEKS = 604800
|
|
756
|
+
YEARS = 31536000
|
|
757
|
+
|
|
656
758
|
def __init__(self, config: Dict, playbook_compatible=False) -> None:
|
|
657
759
|
sys.excepthook = killProgramHook
|
|
658
760
|
|
|
@@ -677,6 +779,15 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
|
|
|
677
779
|
self.connect_type = get_config_variable(
|
|
678
780
|
"CONNECTOR_TYPE", ["connector", "type"], config
|
|
679
781
|
)
|
|
782
|
+
self.connect_queue_threshold = get_config_variable(
|
|
783
|
+
"CONNECTOR_QUEUE_THRESHOLD",
|
|
784
|
+
["connector", "queue_threshold"],
|
|
785
|
+
config,
|
|
786
|
+
default=500, # Mo
|
|
787
|
+
)
|
|
788
|
+
self.connect_duration_period = get_config_variable(
|
|
789
|
+
"CONNECTOR_DURATION_PERIOD", ["connector", "duration_period"], config
|
|
790
|
+
)
|
|
680
791
|
self.connect_live_stream_id = get_config_variable(
|
|
681
792
|
"CONNECTOR_LIVE_STREAM_ID",
|
|
682
793
|
["connector", "live_stream_id"],
|
|
@@ -775,6 +886,7 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
|
|
|
775
886
|
False,
|
|
776
887
|
False,
|
|
777
888
|
)
|
|
889
|
+
self.scheduler = sched.scheduler(time.time, time.sleep)
|
|
778
890
|
# Start up the server to expose the metrics.
|
|
779
891
|
expose_metrics = get_config_variable(
|
|
780
892
|
"CONNECTOR_EXPOSE_METRICS",
|
|
@@ -786,7 +898,8 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
|
|
|
786
898
|
metrics_port = get_config_variable(
|
|
787
899
|
"CONNECTOR_METRICS_PORT", ["connector", "metrics_port"], config, True, 9095
|
|
788
900
|
)
|
|
789
|
-
|
|
901
|
+
# Initialize ConnectorInfo instance
|
|
902
|
+
self.connector_info = ConnectorInfo()
|
|
790
903
|
# Initialize configuration
|
|
791
904
|
# - Classic API that will be directly attached to the connector rights
|
|
792
905
|
self.api = OpenCTIApiClient(
|
|
@@ -881,15 +994,34 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
|
|
|
881
994
|
|
|
882
995
|
# Start ping thread
|
|
883
996
|
if not self.connect_run_and_terminate:
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
self.
|
|
891
|
-
|
|
892
|
-
|
|
997
|
+
|
|
998
|
+
is_run_and_terminate = False
|
|
999
|
+
if self.connect_duration_period == 0:
|
|
1000
|
+
is_run_and_terminate = True
|
|
1001
|
+
|
|
1002
|
+
if isinstance(self.connect_duration_period, str):
|
|
1003
|
+
if self.connect_duration_period == "0":
|
|
1004
|
+
is_run_and_terminate = True
|
|
1005
|
+
else:
|
|
1006
|
+
# Calculates and validate the duration period in seconds
|
|
1007
|
+
timedelta_adapter = TypeAdapter(datetime.timedelta)
|
|
1008
|
+
td = timedelta_adapter.validate_python(self.connect_duration_period)
|
|
1009
|
+
duration_period_in_seconds = int(td.total_seconds())
|
|
1010
|
+
|
|
1011
|
+
if duration_period_in_seconds == 0:
|
|
1012
|
+
is_run_and_terminate = True
|
|
1013
|
+
|
|
1014
|
+
if self.connect_duration_period is None or not is_run_and_terminate:
|
|
1015
|
+
self.ping = PingAlive(
|
|
1016
|
+
self.connector_logger,
|
|
1017
|
+
self.connector.id,
|
|
1018
|
+
self.api,
|
|
1019
|
+
self.get_state,
|
|
1020
|
+
self.set_state,
|
|
1021
|
+
self.metric,
|
|
1022
|
+
self.connector_info,
|
|
1023
|
+
)
|
|
1024
|
+
self.ping.start()
|
|
893
1025
|
|
|
894
1026
|
# self.listen_stream = None
|
|
895
1027
|
self.listen_queue = None
|
|
@@ -972,7 +1104,13 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
|
|
|
972
1104
|
def force_ping(self):
|
|
973
1105
|
try:
|
|
974
1106
|
initial_state = self.get_state()
|
|
975
|
-
|
|
1107
|
+
connector_info = self.connector_info.all_details
|
|
1108
|
+
self.connector_logger.debug(
|
|
1109
|
+
"ForcePing ConnectorInfo", {"connector_info": connector_info}
|
|
1110
|
+
)
|
|
1111
|
+
result = self.api.connector.ping(
|
|
1112
|
+
self.connector_id, initial_state, connector_info
|
|
1113
|
+
)
|
|
976
1114
|
remote_state = (
|
|
977
1115
|
json.loads(result["connector_state"])
|
|
978
1116
|
if result["connector_state"] is not None
|
|
@@ -980,11 +1118,292 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
|
|
|
980
1118
|
else None
|
|
981
1119
|
)
|
|
982
1120
|
if initial_state != remote_state:
|
|
983
|
-
self.api.connector.ping(
|
|
1121
|
+
self.api.connector.ping(
|
|
1122
|
+
self.connector_id, initial_state, connector_info
|
|
1123
|
+
)
|
|
984
1124
|
except Exception as e: # pylint: disable=broad-except
|
|
985
1125
|
self.metric.inc("error_count")
|
|
986
1126
|
self.connector_logger.error("Error pinging the API", {"reason": str(e)})
|
|
987
1127
|
|
|
1128
|
+
def next_run_datetime(self, duration_period_in_seconds: Union[int, float]) -> None:
|
|
1129
|
+
"""
|
|
1130
|
+
Lets you know what the next run of the scheduler will be in iso datetime format
|
|
1131
|
+
|
|
1132
|
+
:param duration_period_in_seconds: Duration in seconds
|
|
1133
|
+
:return: None
|
|
1134
|
+
"""
|
|
1135
|
+
try:
|
|
1136
|
+
duration_timedelta = datetime.timedelta(seconds=duration_period_in_seconds)
|
|
1137
|
+
next_datetime = datetime.datetime.utcnow() + duration_timedelta
|
|
1138
|
+
# Set next_run_datetime
|
|
1139
|
+
self.connector_info.next_run_datetime = next_datetime.strftime(
|
|
1140
|
+
"%Y-%m-%dT%H:%M:%SZ"
|
|
1141
|
+
)
|
|
1142
|
+
self.connector_logger.info(
|
|
1143
|
+
"[INFO] Schedule next run of connector: ",
|
|
1144
|
+
{"next_run_datetime": self.connector_info.next_run_datetime},
|
|
1145
|
+
)
|
|
1146
|
+
return
|
|
1147
|
+
except Exception as err:
|
|
1148
|
+
self.metric.inc("error_count")
|
|
1149
|
+
self.connector_logger.error(
|
|
1150
|
+
"[ERROR] An error occurred while calculating the next run in datetime",
|
|
1151
|
+
{"reason": str(err)},
|
|
1152
|
+
)
|
|
1153
|
+
sys.excepthook(*sys.exc_info())
|
|
1154
|
+
|
|
1155
|
+
def last_run_datetime(self) -> None:
|
|
1156
|
+
"""
|
|
1157
|
+
Lets you know what the last run of the connector the scheduler processed, will be in iso datetime format
|
|
1158
|
+
|
|
1159
|
+
:return: None
|
|
1160
|
+
"""
|
|
1161
|
+
try:
|
|
1162
|
+
current_datetime = datetime.datetime.utcnow()
|
|
1163
|
+
# Set last_run_datetime
|
|
1164
|
+
self.connector_info.last_run_datetime = current_datetime.strftime(
|
|
1165
|
+
"%Y-%m-%dT%H:%M:%SZ"
|
|
1166
|
+
)
|
|
1167
|
+
return
|
|
1168
|
+
except Exception as err:
|
|
1169
|
+
self.metric.inc("error_count")
|
|
1170
|
+
self.connector_logger.error(
|
|
1171
|
+
"[ERROR] An error occurred while converting the last run in datetime",
|
|
1172
|
+
{"reason": str(err)},
|
|
1173
|
+
)
|
|
1174
|
+
sys.excepthook(*sys.exc_info())
|
|
1175
|
+
|
|
1176
|
+
def check_connector_buffering(self) -> bool:
|
|
1177
|
+
"""
|
|
1178
|
+
Lets you know if the RabbitMQ queue has exceeded the allowed threshold defined by the connector or not
|
|
1179
|
+
:return: boolean
|
|
1180
|
+
"""
|
|
1181
|
+
try:
|
|
1182
|
+
connector_details = self.api.connector.read(connector_id=self.connector_id)
|
|
1183
|
+
|
|
1184
|
+
if connector_details and connector_details.get("id") is not None:
|
|
1185
|
+
connector_queue_id = connector_details["id"]
|
|
1186
|
+
connector_queue_details = connector_details["connector_queue_details"]
|
|
1187
|
+
|
|
1188
|
+
queue_messages_size_byte = connector_queue_details["messages_size"]
|
|
1189
|
+
queue_threshold = self.connect_queue_threshold
|
|
1190
|
+
|
|
1191
|
+
# Convert queue_messages_size to Mo (decimal)
|
|
1192
|
+
queue_messages_size_mo = queue_messages_size_byte / 1000000
|
|
1193
|
+
|
|
1194
|
+
self.connector_logger.debug(
|
|
1195
|
+
"[DEBUG] Connector queue details ...",
|
|
1196
|
+
{
|
|
1197
|
+
"connector_queue_id": connector_queue_id,
|
|
1198
|
+
"queue_threshold": queue_threshold,
|
|
1199
|
+
"messages_number": connector_queue_details["messages_number"],
|
|
1200
|
+
"queue_messages_size": queue_messages_size_mo,
|
|
1201
|
+
},
|
|
1202
|
+
)
|
|
1203
|
+
|
|
1204
|
+
# Set the connector info
|
|
1205
|
+
self.connector_info.queue_messages_size = queue_messages_size_mo
|
|
1206
|
+
self.connector_info.queue_threshold = queue_threshold
|
|
1207
|
+
|
|
1208
|
+
if float(queue_messages_size_mo) < float(queue_threshold):
|
|
1209
|
+
# Set buffering
|
|
1210
|
+
self.connector_info.buffering = False
|
|
1211
|
+
return False
|
|
1212
|
+
else:
|
|
1213
|
+
self.connector_logger.info(
|
|
1214
|
+
"[INFO] Connector will not run until the queue messages size is reduced under queue threshold"
|
|
1215
|
+
)
|
|
1216
|
+
# Set buffering
|
|
1217
|
+
self.connector_info.buffering = True
|
|
1218
|
+
return True
|
|
1219
|
+
|
|
1220
|
+
else:
|
|
1221
|
+
self.metric.inc("error_count")
|
|
1222
|
+
self.connector_logger.error(
|
|
1223
|
+
"[ERROR] An error occurred while retrieving connector details"
|
|
1224
|
+
)
|
|
1225
|
+
sys.excepthook(*sys.exc_info())
|
|
1226
|
+
except Exception as err:
|
|
1227
|
+
self.metric.inc("error_count")
|
|
1228
|
+
self.connector_logger.error(
|
|
1229
|
+
"[ERROR] An error occurred while checking the queue size",
|
|
1230
|
+
{"reason": str(err)},
|
|
1231
|
+
)
|
|
1232
|
+
sys.excepthook(*sys.exc_info())
|
|
1233
|
+
|
|
1234
|
+
def schedule_unit(
|
|
1235
|
+
self,
|
|
1236
|
+
message_callback: Callable[[], None],
|
|
1237
|
+
duration_period: Union[int, float, str],
|
|
1238
|
+
time_unit: TimeUnit,
|
|
1239
|
+
) -> None:
|
|
1240
|
+
"""
|
|
1241
|
+
This (deprecated) method is there to manage backward compatibility of intervals on connectors,
|
|
1242
|
+
allows you to calculate the duration period of connectors in seconds with time_unit and will be
|
|
1243
|
+
replaced by the "schedule_iso" method. It uses a TimeUnit enum.
|
|
1244
|
+
|
|
1245
|
+
:param message_callback: Corresponds to the connector process
|
|
1246
|
+
:param duration_period: Corresponds to the connector interval, it can vary depending on the connector
|
|
1247
|
+
configuration.
|
|
1248
|
+
:param time_unit: The unit of time for the duration_period.
|
|
1249
|
+
Enum TimeUnit Valid (YEARS, WEEKS, DAYS, HOURS, MINUTES, SECONDS)
|
|
1250
|
+
:return: None
|
|
1251
|
+
"""
|
|
1252
|
+
try:
|
|
1253
|
+
# Calculates the duration period in seconds
|
|
1254
|
+
time_unit_in_seconds = time_unit.value
|
|
1255
|
+
duration_period_in_seconds = float(duration_period) * time_unit_in_seconds
|
|
1256
|
+
|
|
1257
|
+
# Start schedule_process
|
|
1258
|
+
self.schedule_process(message_callback, duration_period_in_seconds)
|
|
1259
|
+
|
|
1260
|
+
except Exception as err:
|
|
1261
|
+
self.metric.inc("error_count")
|
|
1262
|
+
self.connector_logger.error(
|
|
1263
|
+
"[ERROR] An unexpected error occurred during schedule_unit",
|
|
1264
|
+
{"reason": str(err)},
|
|
1265
|
+
)
|
|
1266
|
+
sys.excepthook(*sys.exc_info())
|
|
1267
|
+
|
|
1268
|
+
def schedule_iso(
|
|
1269
|
+
self, message_callback: Callable[[], None], duration_period: str
|
|
1270
|
+
) -> None:
|
|
1271
|
+
"""
|
|
1272
|
+
This method allows you to calculate the duration period of connectors in seconds from ISO 8601 format
|
|
1273
|
+
and start the scheduler process.
|
|
1274
|
+
|
|
1275
|
+
:param message_callback: Corresponds to the connector process
|
|
1276
|
+
:param duration_period: Corresponds to a string in ISO 8601 format "P18Y9W4DT11H9M8S"
|
|
1277
|
+
:return: None
|
|
1278
|
+
"""
|
|
1279
|
+
try:
|
|
1280
|
+
if duration_period == "0":
|
|
1281
|
+
duration_period_in_seconds = 0
|
|
1282
|
+
else:
|
|
1283
|
+
# Calculates and validate the duration period in seconds
|
|
1284
|
+
timedelta_adapter = TypeAdapter(datetime.timedelta)
|
|
1285
|
+
td = timedelta_adapter.validate_python(duration_period)
|
|
1286
|
+
duration_period_in_seconds = int(td.total_seconds())
|
|
1287
|
+
|
|
1288
|
+
# Start schedule_process
|
|
1289
|
+
self.schedule_process(message_callback, duration_period_in_seconds)
|
|
1290
|
+
|
|
1291
|
+
except Exception as err:
|
|
1292
|
+
self.metric.inc("error_count")
|
|
1293
|
+
self.connector_logger.error(
|
|
1294
|
+
"[ERROR] An unexpected error occurred during schedule_iso",
|
|
1295
|
+
{"reason": str(err)},
|
|
1296
|
+
)
|
|
1297
|
+
sys.excepthook(*sys.exc_info())
|
|
1298
|
+
|
|
1299
|
+
def _schedule_process(
|
|
1300
|
+
self,
|
|
1301
|
+
scheduler: sched.scheduler,
|
|
1302
|
+
message_callback: Callable[[], None],
|
|
1303
|
+
duration_period: Union[int, float],
|
|
1304
|
+
) -> None:
|
|
1305
|
+
"""
|
|
1306
|
+
When scheduling, the function retrieves the details of the connector queue,
|
|
1307
|
+
and the connector process starts only if the size of the queue messages is less than or
|
|
1308
|
+
equal to the queue_threshold variable.
|
|
1309
|
+
|
|
1310
|
+
:param scheduler: Scheduler contains a list of all tasks to be started
|
|
1311
|
+
:param message_callback: Corresponds to the connector process
|
|
1312
|
+
:param duration_period: Corresponds to the connector's interval
|
|
1313
|
+
:return: None
|
|
1314
|
+
"""
|
|
1315
|
+
try:
|
|
1316
|
+
self.connector_logger.info("[INFO] Starting schedule")
|
|
1317
|
+
check_connector_buffering = self.check_connector_buffering()
|
|
1318
|
+
|
|
1319
|
+
if not check_connector_buffering:
|
|
1320
|
+
# Start running the connector
|
|
1321
|
+
message_callback()
|
|
1322
|
+
# Lets you know what is the last run of the connector datetime
|
|
1323
|
+
self.last_run_datetime()
|
|
1324
|
+
|
|
1325
|
+
except Exception as err:
|
|
1326
|
+
self.metric.inc("error_count")
|
|
1327
|
+
self.connector_logger.error(
|
|
1328
|
+
"[ERROR] An error occurred while checking the queue size",
|
|
1329
|
+
{"reason": str(err)},
|
|
1330
|
+
)
|
|
1331
|
+
sys.excepthook(*sys.exc_info())
|
|
1332
|
+
|
|
1333
|
+
finally:
|
|
1334
|
+
# Lets you know what the next run of the scheduler will be
|
|
1335
|
+
self.next_run_datetime(duration_period)
|
|
1336
|
+
# Then schedule the next execution
|
|
1337
|
+
scheduler.enter(
|
|
1338
|
+
duration_period,
|
|
1339
|
+
1,
|
|
1340
|
+
self._schedule_process,
|
|
1341
|
+
(scheduler, message_callback, duration_period),
|
|
1342
|
+
)
|
|
1343
|
+
|
|
1344
|
+
def schedule_process(
|
|
1345
|
+
self, message_callback: Callable[[], None], duration_period: Union[int, float]
|
|
1346
|
+
) -> None:
|
|
1347
|
+
"""
|
|
1348
|
+
This method schedules the execution of a connector process.
|
|
1349
|
+
If `duration_period' is zero or `self.connect_run_and_terminate' is True, the process will run and terminate.
|
|
1350
|
+
Otherwise, it schedules the next run based on the interval.
|
|
1351
|
+
|
|
1352
|
+
:param message_callback: Corresponds to the connector process
|
|
1353
|
+
:param duration_period: Corresponds to the connector's interval in seconds
|
|
1354
|
+
:return: None
|
|
1355
|
+
"""
|
|
1356
|
+
try:
|
|
1357
|
+
# In the case where the duration_period_converted is zero, we consider it to be a run and terminate
|
|
1358
|
+
if self.connect_run_and_terminate or duration_period == 0:
|
|
1359
|
+
self.connector_logger.info("[INFO] Starting run and terminate")
|
|
1360
|
+
# Set run_and_terminate
|
|
1361
|
+
self.connector_info.run_and_terminate = True
|
|
1362
|
+
check_connector_buffering = self.check_connector_buffering()
|
|
1363
|
+
|
|
1364
|
+
if not check_connector_buffering:
|
|
1365
|
+
# Start running the connector
|
|
1366
|
+
message_callback()
|
|
1367
|
+
|
|
1368
|
+
# Lets you know what is the last run of the connector datetime
|
|
1369
|
+
self.last_run_datetime()
|
|
1370
|
+
self.connector_logger.info("[INFO] Closing run and terminate")
|
|
1371
|
+
self.force_ping()
|
|
1372
|
+
sys.exit(0)
|
|
1373
|
+
else:
|
|
1374
|
+
# Start running the connector
|
|
1375
|
+
message_callback()
|
|
1376
|
+
# Set queue_threshold and queue_messages_size for the first run
|
|
1377
|
+
self.check_connector_buffering()
|
|
1378
|
+
# Lets you know what is the last run of the connector datetime
|
|
1379
|
+
self.last_run_datetime()
|
|
1380
|
+
# Lets you know what the next run of the scheduler will be
|
|
1381
|
+
self.next_run_datetime(duration_period)
|
|
1382
|
+
|
|
1383
|
+
# Then schedule the next execution
|
|
1384
|
+
self.scheduler.enter(
|
|
1385
|
+
duration_period,
|
|
1386
|
+
1,
|
|
1387
|
+
self._schedule_process,
|
|
1388
|
+
(self.scheduler, message_callback, duration_period),
|
|
1389
|
+
)
|
|
1390
|
+
self.scheduler.run()
|
|
1391
|
+
|
|
1392
|
+
except SystemExit:
|
|
1393
|
+
self.connector_logger.info("SystemExit caught, stopping the scheduler")
|
|
1394
|
+
if self.connect_run_and_terminate:
|
|
1395
|
+
self.connector_logger.info("[INFO] Closing run and terminate")
|
|
1396
|
+
self.force_ping()
|
|
1397
|
+
sys.exit(0)
|
|
1398
|
+
|
|
1399
|
+
except Exception as err:
|
|
1400
|
+
self.metric.inc("error_count")
|
|
1401
|
+
self.connector_logger.error(
|
|
1402
|
+
"[ERROR] An unexpected error occurred during schedule",
|
|
1403
|
+
{"reason": str(err)},
|
|
1404
|
+
)
|
|
1405
|
+
sys.excepthook(*sys.exc_info())
|
|
1406
|
+
|
|
988
1407
|
def listen(
|
|
989
1408
|
self,
|
|
990
1409
|
message_callback: Callable[[Dict], str],
|
|
@@ -359,6 +359,9 @@ class StixCoreObject:
|
|
|
359
359
|
x_opencti_cvss_attack_vector
|
|
360
360
|
x_opencti_cvss_integrity_impact
|
|
361
361
|
x_opencti_cvss_availability_impact
|
|
362
|
+
x_opencti_cisa_kev
|
|
363
|
+
x_opencti_epss_score
|
|
364
|
+
x_opencti_epss_percentile
|
|
362
365
|
}
|
|
363
366
|
... on Incident {
|
|
364
367
|
name
|
|
@@ -1023,6 +1026,9 @@ class StixCoreObject:
|
|
|
1023
1026
|
x_opencti_cvss_attack_vector
|
|
1024
1027
|
x_opencti_cvss_integrity_impact
|
|
1025
1028
|
x_opencti_cvss_availability_impact
|
|
1029
|
+
x_opencti_cisa_kev
|
|
1030
|
+
x_opencti_epss_score
|
|
1031
|
+
x_opencti_epss_percentile
|
|
1026
1032
|
}
|
|
1027
1033
|
... on Incident {
|
|
1028
1034
|
name
|
|
@@ -482,6 +482,9 @@ class StixDomainObject:
|
|
|
482
482
|
x_opencti_cvss_attack_vector
|
|
483
483
|
x_opencti_cvss_integrity_impact
|
|
484
484
|
x_opencti_cvss_availability_impact
|
|
485
|
+
x_opencti_cisa_kev
|
|
486
|
+
x_opencti_epss_score
|
|
487
|
+
x_opencti_epss_percentile
|
|
485
488
|
}
|
|
486
489
|
... on Incident {
|
|
487
490
|
name
|
|
@@ -977,6 +980,9 @@ class StixDomainObject:
|
|
|
977
980
|
x_opencti_cvss_attack_vector
|
|
978
981
|
x_opencti_cvss_integrity_impact
|
|
979
982
|
x_opencti_cvss_availability_impact
|
|
983
|
+
x_opencti_cisa_kev
|
|
984
|
+
x_opencti_epss_score
|
|
985
|
+
x_opencti_epss_percentile
|
|
980
986
|
}
|
|
981
987
|
... on Incident {
|
|
982
988
|
name
|
|
@@ -298,6 +298,9 @@ class StixObjectOrStixRelationship:
|
|
|
298
298
|
x_opencti_cvss_attack_vector
|
|
299
299
|
x_opencti_cvss_integrity_impact
|
|
300
300
|
x_opencti_cvss_availability_impact
|
|
301
|
+
x_opencti_cisa_kev
|
|
302
|
+
x_opencti_epss_score
|
|
303
|
+
x_opencti_epss_percentile
|
|
301
304
|
}
|
|
302
305
|
... on Incident {
|
|
303
306
|
name
|
|
@@ -106,6 +106,8 @@ class Vulnerability:
|
|
|
106
106
|
x_opencti_cvss_availability_impact
|
|
107
107
|
x_opencti_cvss_confidentiality_impact
|
|
108
108
|
x_opencti_cisa_kev
|
|
109
|
+
x_opencti_epss_score
|
|
110
|
+
x_opencti_epss_percentile
|
|
109
111
|
importFiles {
|
|
110
112
|
edges {
|
|
111
113
|
node {
|
|
@@ -285,6 +287,8 @@ class Vulnerability:
|
|
|
285
287
|
x_opencti_cvss_base_severity = kwargs.get("x_opencti_cvss_base_severity", None)
|
|
286
288
|
x_opencti_cvss_attack_vector = kwargs.get("x_opencti_cvss_attack_vector", None)
|
|
287
289
|
x_opencti_cisa_kev = kwargs.get("x_opencti_cisa_kev", None)
|
|
290
|
+
x_opencti_epss_score = kwargs.get("x_opencti_epss_score", None)
|
|
291
|
+
x_opencti_epss_percentile = kwargs.get("x_opencti_epss_percentile", None)
|
|
288
292
|
x_opencti_cvss_integrity_impact = kwargs.get(
|
|
289
293
|
"x_opencti_cvss_integrity_impact", None
|
|
290
294
|
)
|
|
@@ -336,6 +340,8 @@ class Vulnerability:
|
|
|
336
340
|
"x_opencti_cvss_availability_impact": x_opencti_cvss_availability_impact,
|
|
337
341
|
"x_opencti_cvss_confidentiality_impact": x_opencti_cvss_confidentiality_impact,
|
|
338
342
|
"x_opencti_cisa_kev": x_opencti_cisa_kev,
|
|
343
|
+
"x_opencti_epss_score": x_opencti_epss_score,
|
|
344
|
+
"x_opencti_epss_percentile": x_opencti_epss_percentile,
|
|
339
345
|
"x_opencti_stix_ids": x_opencti_stix_ids,
|
|
340
346
|
"x_opencti_workflow_id": x_opencti_workflow_id,
|
|
341
347
|
"update": update,
|
|
@@ -444,6 +450,16 @@ class Vulnerability:
|
|
|
444
450
|
stix_object["x_opencti_cisa_kev"] = (
|
|
445
451
|
self.opencti.get_attribute_in_extension("cisa_kev", stix_object)
|
|
446
452
|
)
|
|
453
|
+
if "x_opencti_epss_score" not in stix_object:
|
|
454
|
+
stix_object["x_opencti_epss_score"] = (
|
|
455
|
+
self.opencti.get_attribute_in_extension("epss_score", stix_object)
|
|
456
|
+
)
|
|
457
|
+
if "x_opencti_epss_percentile" not in stix_object:
|
|
458
|
+
stix_object["x_opencti_epss_percentile"] = (
|
|
459
|
+
self.opencti.get_attribute_in_extension(
|
|
460
|
+
"epss_percentile", stix_object
|
|
461
|
+
)
|
|
462
|
+
)
|
|
447
463
|
|
|
448
464
|
return self.create(
|
|
449
465
|
stix_id=stix_object["id"],
|
|
@@ -531,6 +547,16 @@ class Vulnerability:
|
|
|
531
547
|
if "x_opencti_cisa_kev" in stix_object
|
|
532
548
|
else None
|
|
533
549
|
),
|
|
550
|
+
x_opencti_epss_score=(
|
|
551
|
+
stix_object["x_opencti_epss_score"]
|
|
552
|
+
if "x_opencti_epss_score" in stix_object
|
|
553
|
+
else None
|
|
554
|
+
),
|
|
555
|
+
x_opencti_epss_percentile=(
|
|
556
|
+
stix_object["x_opencti_epss_percentile"]
|
|
557
|
+
if "x_opencti_epss_percentile" in stix_object
|
|
558
|
+
else None
|
|
559
|
+
),
|
|
534
560
|
update=update,
|
|
535
561
|
)
|
|
536
562
|
else:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pycti
|
|
3
|
-
Version: 6.2.
|
|
3
|
+
Version: 6.2.12
|
|
4
4
|
Summary: Python API client for OpenCTI.
|
|
5
5
|
Home-page: https://github.com/OpenCTI-Platform/client-python
|
|
6
6
|
Author: Filigran
|
|
@@ -21,37 +21,38 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
21
21
|
Requires-Python: >=3.7
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
23
|
License-File: LICENSE
|
|
24
|
-
Requires-Dist: datefinder
|
|
25
|
-
Requires-Dist: pika
|
|
26
|
-
Requires-Dist:
|
|
27
|
-
Requires-Dist:
|
|
28
|
-
Requires-Dist:
|
|
29
|
-
Requires-Dist:
|
|
30
|
-
Requires-Dist:
|
|
31
|
-
Requires-Dist:
|
|
32
|
-
Requires-Dist:
|
|
33
|
-
Requires-Dist: opentelemetry-
|
|
34
|
-
Requires-Dist:
|
|
35
|
-
Requires-Dist:
|
|
36
|
-
Requires-Dist:
|
|
37
|
-
Requires-Dist:
|
|
38
|
-
Requires-Dist: python-magic
|
|
24
|
+
Requires-Dist: datefinder~=0.7.3
|
|
25
|
+
Requires-Dist: pika~=1.3.0
|
|
26
|
+
Requires-Dist: pydantic~=2.8.2
|
|
27
|
+
Requires-Dist: python-json-logger~=2.0.4
|
|
28
|
+
Requires-Dist: PyYAML~=6.0
|
|
29
|
+
Requires-Dist: requests~=2.32.2
|
|
30
|
+
Requires-Dist: setuptools~=71.1.0
|
|
31
|
+
Requires-Dist: cachetools~=5.4.0
|
|
32
|
+
Requires-Dist: prometheus-client~=0.20.0
|
|
33
|
+
Requires-Dist: opentelemetry-api~=1.22.0
|
|
34
|
+
Requires-Dist: opentelemetry-sdk~=1.22.0
|
|
35
|
+
Requires-Dist: deprecation~=2.1.0
|
|
36
|
+
Requires-Dist: filigran-sseclient~=1.0.0
|
|
37
|
+
Requires-Dist: stix2~=3.0.1
|
|
38
|
+
Requires-Dist: python-magic~=0.4.27; sys_platform == "linux" or sys_platform == "darwin"
|
|
39
|
+
Requires-Dist: python-magic-bin~=0.4.14; sys_platform == "win32"
|
|
39
40
|
Provides-Extra: dev
|
|
40
|
-
Requires-Dist: black
|
|
41
|
-
Requires-Dist: build
|
|
42
|
-
Requires-Dist: isort
|
|
43
|
-
Requires-Dist: types-pytz
|
|
44
|
-
Requires-Dist: pre-commit
|
|
45
|
-
Requires-Dist: pytest-cases
|
|
46
|
-
Requires-Dist: pytest-cov
|
|
47
|
-
Requires-Dist: pytest-randomly
|
|
48
|
-
Requires-Dist: pytest
|
|
49
|
-
Requires-Dist: types-python-dateutil
|
|
50
|
-
Requires-Dist: wheel
|
|
41
|
+
Requires-Dist: black~=24.4.0; extra == "dev"
|
|
42
|
+
Requires-Dist: build~=1.2.1; extra == "dev"
|
|
43
|
+
Requires-Dist: isort~=5.13.0; extra == "dev"
|
|
44
|
+
Requires-Dist: types-pytz~=2024.1.0.20240203; extra == "dev"
|
|
45
|
+
Requires-Dist: pre-commit~=3.7.0; extra == "dev"
|
|
46
|
+
Requires-Dist: pytest-cases~=3.8.0; extra == "dev"
|
|
47
|
+
Requires-Dist: pytest-cov~=5.0.0; extra == "dev"
|
|
48
|
+
Requires-Dist: pytest-randomly~=3.15.0; extra == "dev"
|
|
49
|
+
Requires-Dist: pytest~=8.2.0; extra == "dev"
|
|
50
|
+
Requires-Dist: types-python-dateutil~=2.9.0; extra == "dev"
|
|
51
|
+
Requires-Dist: wheel~=0.43.0; extra == "dev"
|
|
51
52
|
Provides-Extra: doc
|
|
52
|
-
Requires-Dist: autoapi
|
|
53
|
-
Requires-Dist: sphinx-autodoc-typehints
|
|
54
|
-
Requires-Dist: sphinx-rtd-theme
|
|
53
|
+
Requires-Dist: autoapi~=2.0.1; extra == "doc"
|
|
54
|
+
Requires-Dist: sphinx-autodoc-typehints~=2.2.2; extra == "doc"
|
|
55
|
+
Requires-Dist: sphinx-rtd-theme~=2.0.0; extra == "doc"
|
|
55
56
|
|
|
56
57
|
# OpenCTI client for Python
|
|
57
58
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
pycti/__init__.py,sha256=
|
|
1
|
+
pycti/__init__.py,sha256=i_2D3nsA-HhDqHhqa6Ok87xfr86XRK7DGVznZY7Mq-g,5219
|
|
2
2
|
pycti/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
3
|
pycti/api/opencti_api_client.py,sha256=c-xyfmas2LkYNIY8j4-dHBJepHNL6uNhageyJOvYdQc,29834
|
|
4
|
-
pycti/api/opencti_api_connector.py,sha256=
|
|
4
|
+
pycti/api/opencti_api_connector.py,sha256=a2WknPjrlM9NkkbJXKaRoPWaY0ITMlwep6lvnGlSzMk,5299
|
|
5
5
|
pycti/api/opencti_api_playbook.py,sha256=456We78vESukfSOi_CctfZ9dbBJEi76EHClRc2f21Js,1628
|
|
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=5oVvS27KWhzRiofJAeQPDtba-EP83FziSistyEd5l-U,2561
|
|
9
|
-
pycti/connector/opencti_connector_helper.py,sha256=
|
|
9
|
+
pycti/connector/opencti_connector_helper.py,sha256=sJocY7JQoTVGDj_Lnl2sM5n3Lra_KqbCaDrLvgJgjhA,78321
|
|
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=AP_lTsHk0NscvFsXdxjzXWLCpg0ZaHTUT-Yg2JyB4YU,22047
|
|
@@ -40,12 +40,12 @@ pycti/entities/opencti_observed_data.py,sha256=123sp3_Xbu1HVZW5-5_Tu8DYo_wBf6RVG
|
|
|
40
40
|
pycti/entities/opencti_opinion.py,sha256=SPcY8-0zRJCMle-eDLka-CFPyAqU3CnVVBtfVYhzyJE,21837
|
|
41
41
|
pycti/entities/opencti_report.py,sha256=y-in19XqQ1BN0MZjKfxiOWe_0Q4Wakrj2MLrqzFpokU,35224
|
|
42
42
|
pycti/entities/opencti_stix.py,sha256=uMheSg8i1f2Ozx2Mk0iShWzHHjj6MMWDtV5nDjVxKEE,2275
|
|
43
|
-
pycti/entities/opencti_stix_core_object.py,sha256=
|
|
43
|
+
pycti/entities/opencti_stix_core_object.py,sha256=cfTOy77mxbklx_xpbJsI1YGayhV2c5lDgYSUmsvKn3Q,50788
|
|
44
44
|
pycti/entities/opencti_stix_core_relationship.py,sha256=ldhS0rJ9L6LrY1wvX28f9TMpVD1rVxsK-GYhH06qJFo,43944
|
|
45
45
|
pycti/entities/opencti_stix_cyber_observable.py,sha256=TMdBMLJLcmfdVyQtzjb0CEcVts6avqgStWGFoxH6OLQ,91250
|
|
46
|
-
pycti/entities/opencti_stix_domain_object.py,sha256=
|
|
46
|
+
pycti/entities/opencti_stix_domain_object.py,sha256=XAwPHe9tUjtGRZLu6BNdotXxYYjf5_AFIkHLeu6NzFo,78732
|
|
47
47
|
pycti/entities/opencti_stix_nested_ref_relationship.py,sha256=eGCF7yaEDqMsJbgvTDle0tVzRCF78CmEhE02jioUTCc,12605
|
|
48
|
-
pycti/entities/opencti_stix_object_or_stix_relationship.py,sha256=
|
|
48
|
+
pycti/entities/opencti_stix_object_or_stix_relationship.py,sha256=5qutzML6SyYzDhZ-QpI9Vh23hzLEs-xeFAAZOpGHZ2g,18049
|
|
49
49
|
pycti/entities/opencti_stix_sighting_relationship.py,sha256=OPbdanElqnAj7cLu2eh1P5PBh_54xfxnW36HX_5wnEs,28077
|
|
50
50
|
pycti/entities/opencti_task.py,sha256=gDlUVyeRcIlIoh5bKtar-NmFa5_3OmMd4OkRVi_AYco,24612
|
|
51
51
|
pycti/entities/opencti_threat_actor.py,sha256=lRdPhXX_HsNSE5rTwkke_U5T_FAPGD22ow2-YeZdaYc,9950
|
|
@@ -53,7 +53,7 @@ pycti/entities/opencti_threat_actor_group.py,sha256=RLwufTb8vFvOHgV81VXf2cA1sREz
|
|
|
53
53
|
pycti/entities/opencti_threat_actor_individual.py,sha256=b4Zqd40ehrFeaU6f_bSY6343Dg5vq6PuiDtKLXUrv78,19857
|
|
54
54
|
pycti/entities/opencti_tool.py,sha256=5A20xY0fDBNLkbQP_32srjS3FXEVYGNxhe1I1YnaIbA,15237
|
|
55
55
|
pycti/entities/opencti_vocabulary.py,sha256=6JfOByggvSxvkfIXk1b60T7fyWOhxZ6YFkGbSeV8F-4,5988
|
|
56
|
-
pycti/entities/opencti_vulnerability.py,sha256=
|
|
56
|
+
pycti/entities/opencti_vulnerability.py,sha256=B-7H9_gRNDnjfspkAq3o4z8WJg_3EQ7y0iAXa9WHWkg,22547
|
|
57
57
|
pycti/entities/indicator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
58
58
|
pycti/entities/indicator/opencti_indicator_properties.py,sha256=2HJiBkBzbLe75Yu5AeSrTZCSKikPwwM_DeDgJCl7wm4,4953
|
|
59
59
|
pycti/entities/stix_cyber_observable/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -66,8 +66,8 @@ pycti/utils/opencti_stix2.py,sha256=WAVgvTcj8P0QfN_K2-avCeFwSkxVtBz1nh0dA63Nlr4,
|
|
|
66
66
|
pycti/utils/opencti_stix2_splitter.py,sha256=A2GqoiFzEga8hslgA3mm4FDoObFsWgx4zK4DdcWTguc,4907
|
|
67
67
|
pycti/utils/opencti_stix2_update.py,sha256=CnMyqkeVA0jgyxEcgqna8sABU4YPMjkEJ228GVurIn4,14658
|
|
68
68
|
pycti/utils/opencti_stix2_utils.py,sha256=4r9qglN3AIN8JH1B9Ts2o20Qn3K203M4c5-lIPzRpZ4,4138
|
|
69
|
-
pycti-6.2.
|
|
70
|
-
pycti-6.2.
|
|
71
|
-
pycti-6.2.
|
|
72
|
-
pycti-6.2.
|
|
73
|
-
pycti-6.2.
|
|
69
|
+
pycti-6.2.12.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
70
|
+
pycti-6.2.12.dist-info/METADATA,sha256=o6y8iDJfmyfuRON5Vos54H8xA0DZpWdAjJV-LIEIRog,5419
|
|
71
|
+
pycti-6.2.12.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
|
|
72
|
+
pycti-6.2.12.dist-info/top_level.txt,sha256=cqEpxitAhHP4VgSA6xmrak6Yk9MeBkwoMTB6k7d2ZnE,6
|
|
73
|
+
pycti-6.2.12.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|