pycti 6.0.9__tar.gz → 6.0.10__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.

Files changed (73) hide show
  1. {pycti-6.0.9 → pycti-6.0.10}/PKG-INFO +4 -4
  2. {pycti-6.0.9 → pycti-6.0.10}/pycti/__init__.py +11 -1
  3. {pycti-6.0.9 → pycti-6.0.10}/pycti/api/opencti_api_client.py +2 -0
  4. {pycti-6.0.9 → pycti-6.0.10}/pycti/api/opencti_api_work.py +63 -54
  5. {pycti-6.0.9 → pycti-6.0.10}/pycti/connector/opencti_connector_helper.py +139 -40
  6. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_incident.py +4 -0
  7. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_indicator.py +11 -0
  8. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_intrusion_set.py +12 -1
  9. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_malware_analysis.py +11 -0
  10. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_stix_core_object.py +6 -0
  11. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_stix_cyber_observable.py +68 -10
  12. {pycti-6.0.9 → pycti-6.0.10}/pycti/utils/constants.py +110 -0
  13. {pycti-6.0.9 → pycti-6.0.10}/pycti/utils/opencti_stix2.py +15 -0
  14. {pycti-6.0.9 → pycti-6.0.10}/pycti/utils/opencti_stix2_utils.py +12 -1
  15. {pycti-6.0.9 → pycti-6.0.10}/pycti.egg-info/PKG-INFO +4 -4
  16. {pycti-6.0.9 → pycti-6.0.10}/pycti.egg-info/requires.txt +3 -3
  17. {pycti-6.0.9 → pycti-6.0.10}/setup.cfg +3 -3
  18. {pycti-6.0.9 → pycti-6.0.10}/LICENSE +0 -0
  19. {pycti-6.0.9 → pycti-6.0.10}/README.md +0 -0
  20. {pycti-6.0.9 → pycti-6.0.10}/pycti/api/__init__.py +0 -0
  21. {pycti-6.0.9 → pycti-6.0.10}/pycti/api/opencti_api_connector.py +0 -0
  22. {pycti-6.0.9 → pycti-6.0.10}/pycti/api/opencti_api_playbook.py +0 -0
  23. {pycti-6.0.9 → pycti-6.0.10}/pycti/connector/__init__.py +0 -0
  24. {pycti-6.0.9 → pycti-6.0.10}/pycti/connector/opencti_connector.py +0 -0
  25. {pycti-6.0.9 → pycti-6.0.10}/pycti/connector/opencti_metric_handler.py +0 -0
  26. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/__init__.py +0 -0
  27. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_attack_pattern.py +0 -0
  28. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_campaign.py +0 -0
  29. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_case_incident.py +0 -0
  30. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_case_rfi.py +0 -0
  31. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_case_rft.py +0 -0
  32. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_channel.py +0 -0
  33. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_course_of_action.py +0 -0
  34. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_data_component.py +0 -0
  35. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_data_source.py +0 -0
  36. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_event.py +0 -0
  37. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_external_reference.py +0 -0
  38. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_feedback.py +0 -0
  39. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_grouping.py +0 -0
  40. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_identity.py +0 -0
  41. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_infrastructure.py +0 -0
  42. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_kill_chain_phase.py +0 -0
  43. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_label.py +0 -0
  44. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_language.py +0 -0
  45. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_location.py +0 -0
  46. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_malware.py +0 -0
  47. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_marking_definition.py +0 -0
  48. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_narrative.py +0 -0
  49. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_note.py +0 -0
  50. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_observed_data.py +0 -0
  51. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_opinion.py +0 -0
  52. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_report.py +0 -0
  53. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_stix.py +0 -0
  54. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_stix_core_relationship.py +0 -0
  55. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_stix_domain_object.py +0 -0
  56. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_stix_nested_ref_relationship.py +0 -0
  57. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_stix_object_or_stix_relationship.py +0 -0
  58. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_stix_sighting_relationship.py +0 -0
  59. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_task.py +0 -0
  60. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_threat_actor.py +0 -0
  61. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_threat_actor_group.py +0 -0
  62. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_threat_actor_individual.py +0 -0
  63. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_tool.py +0 -0
  64. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_vocabulary.py +0 -0
  65. {pycti-6.0.9 → pycti-6.0.10}/pycti/entities/opencti_vulnerability.py +0 -0
  66. {pycti-6.0.9 → pycti-6.0.10}/pycti/utils/__init__.py +0 -0
  67. {pycti-6.0.9 → pycti-6.0.10}/pycti/utils/opencti_logger.py +0 -0
  68. {pycti-6.0.9 → pycti-6.0.10}/pycti/utils/opencti_stix2_splitter.py +0 -0
  69. {pycti-6.0.9 → pycti-6.0.10}/pycti/utils/opencti_stix2_update.py +0 -0
  70. {pycti-6.0.9 → pycti-6.0.10}/pycti.egg-info/SOURCES.txt +0 -0
  71. {pycti-6.0.9 → pycti-6.0.10}/pycti.egg-info/dependency_links.txt +0 -0
  72. {pycti-6.0.9 → pycti-6.0.10}/pycti.egg-info/top_level.txt +0 -0
  73. {pycti-6.0.9 → pycti-6.0.10}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pycti
3
- Version: 6.0.9
3
+ Version: 6.0.10
4
4
  Summary: Python API client for OpenCTI.
5
5
  Home-page: https://github.com/OpenCTI-Platform/client-python
6
6
  Author: Filigran
@@ -29,12 +29,12 @@ Requires-Dist: python-magic-bin~=0.4.14; sys_platform == "win32"
29
29
  Requires-Dist: python_json_logger~=2.0.4
30
30
  Requires-Dist: pyyaml~=6.0
31
31
  Requires-Dist: requests~=2.31.0
32
- Requires-Dist: setuptools~=69.2.0
32
+ Requires-Dist: setuptools~=69.5.1
33
33
  Requires-Dist: filigran-sseclient~=1.0.0
34
34
  Requires-Dist: stix2~=3.0.1
35
35
  Requires-Dist: cachetools~=5.3.0
36
36
  Provides-Extra: dev
37
- Requires-Dist: black~=24.3.0; extra == "dev"
37
+ Requires-Dist: black~=24.4.0; extra == "dev"
38
38
  Requires-Dist: build~=1.2.1; extra == "dev"
39
39
  Requires-Dist: isort~=5.13.0; extra == "dev"
40
40
  Requires-Dist: types-pytz~=2024.1.0.20240203; extra == "dev"
@@ -47,7 +47,7 @@ Requires-Dist: types-python-dateutil~=2.9.0; extra == "dev"
47
47
  Requires-Dist: wheel~=0.43.0; extra == "dev"
48
48
  Provides-Extra: doc
49
49
  Requires-Dist: autoapi~=2.0.1; extra == "doc"
50
- Requires-Dist: sphinx-autodoc-typehints~=2.0.0; extra == "doc"
50
+ Requires-Dist: sphinx-autodoc-typehints~=2.1.0; extra == "doc"
51
51
  Requires-Dist: sphinx-rtd-theme~=2.0.0; extra == "doc"
52
52
 
53
53
  # OpenCTI client for Python
@@ -1,5 +1,5 @@
1
1
  # -*- coding: utf-8 -*-
2
- __version__ = "6.0.9"
2
+ __version__ = "6.0.10"
3
3
 
4
4
  from .api.opencti_api_client import OpenCTIApiClient
5
5
  from .api.opencti_api_connector import OpenCTIApiConnector
@@ -53,9 +53,14 @@ from .entities.opencti_vulnerability import Vulnerability
53
53
  from .utils.constants import (
54
54
  CustomObjectCaseIncident,
55
55
  CustomObjectTask,
56
+ CustomObservableBankAccount,
57
+ CustomObservableCredential,
56
58
  CustomObservableCryptocurrencyWallet,
57
59
  CustomObservableHostname,
60
+ CustomObservablePaymentCard,
61
+ CustomObservablePhoneNumber,
58
62
  CustomObservableText,
63
+ CustomObservableTrackingNumber,
59
64
  CustomObservableUserAgent,
60
65
  MultipleRefRelationship,
61
66
  StixCyberObservableTypes,
@@ -128,9 +133,14 @@ __all__ = [
128
133
  "CustomObjectCaseIncident",
129
134
  "CustomObjectTask",
130
135
  "StixCyberObservableTypes",
136
+ "CustomObservableCredential",
131
137
  "CustomObservableHostname",
132
138
  "CustomObservableUserAgent",
139
+ "CustomObservableBankAccount",
133
140
  "CustomObservableCryptocurrencyWallet",
141
+ "CustomObservablePaymentCard",
142
+ "CustomObservablePhoneNumber",
143
+ "CustomObservableTrackingNumber",
134
144
  "CustomObservableText",
135
145
  "STIX_EXT_MITRE",
136
146
  "STIX_EXT_OCTI_SCO",
@@ -108,6 +108,7 @@ class OpenCTIApiClient:
108
108
  ssl_verify=False,
109
109
  proxies=None,
110
110
  json_logging=False,
111
+ bundle_send_to_queue=True,
111
112
  cert=None,
112
113
  auth=None,
113
114
  perform_health_check=True,
@@ -115,6 +116,7 @@ class OpenCTIApiClient:
115
116
  """Constructor method"""
116
117
 
117
118
  # Check configuration
119
+ self.bundle_send_to_queue = bundle_send_to_queue
118
120
  self.ssl_verify = ssl_verify
119
121
  self.cert = cert
120
122
  self.proxies = proxies
@@ -9,28 +9,34 @@ class OpenCTIApiWork:
9
9
  self.api = api
10
10
 
11
11
  def to_received(self, work_id: str, message: str):
12
- self.api.app_logger.info("Reporting work update_received", {"work_id": work_id})
13
- query = """
14
- mutation workToReceived($id: ID!, $message: String) {
15
- workEdit(id: $id) {
16
- toReceived (message: $message)
12
+ if self.api.bundle_send_to_queue:
13
+ self.api.app_logger.info(
14
+ "Reporting work update_received", {"work_id": work_id}
15
+ )
16
+ query = """
17
+ mutation workToReceived($id: ID!, $message: String) {
18
+ workEdit(id: $id) {
19
+ toReceived (message: $message)
20
+ }
17
21
  }
18
- }
19
- """
20
- self.api.query(query, {"id": work_id, "message": message})
22
+ """
23
+ self.api.query(query, {"id": work_id, "message": message})
21
24
 
22
25
  def to_processed(self, work_id: str, message: str, in_error: bool = False):
23
- self.api.app_logger.info(
24
- "Reporting work update_processed", {"work_id": work_id}
25
- )
26
- query = """
27
- mutation workToProcessed($id: ID!, $message: String, $inError: Boolean) {
28
- workEdit(id: $id) {
29
- toProcessed (message: $message, inError: $inError)
26
+ if self.api.bundle_send_to_queue:
27
+ self.api.app_logger.info(
28
+ "Reporting work update_processed", {"work_id": work_id}
29
+ )
30
+ query = """
31
+ mutation workToProcessed($id: ID!, $message: String, $inError: Boolean) {
32
+ workEdit(id: $id) {
33
+ toProcessed (message: $message, inError: $inError)
34
+ }
30
35
  }
31
- }
32
- """
33
- self.api.query(query, {"id": work_id, "message": message, "inError": in_error})
36
+ """
37
+ self.api.query(
38
+ query, {"id": work_id, "message": message, "inError": in_error}
39
+ )
34
40
 
35
41
  def ping(self, work_id: str):
36
42
  self.api.app_logger.info("Ping work", {"work_id": work_id})
@@ -44,49 +50,52 @@ class OpenCTIApiWork:
44
50
  self.api.query(query, {"id": work_id})
45
51
 
46
52
  def report_expectation(self, work_id: str, error):
47
- self.api.app_logger.info("Report expectation", {"work_id": work_id})
48
- query = """
49
- mutation reportExpectation($id: ID!, $error: WorkErrorInput) {
50
- workEdit(id: $id) {
51
- reportExpectation(error: $error)
53
+ if self.api.bundle_send_to_queue:
54
+ self.api.app_logger.info("Report expectation", {"work_id": work_id})
55
+ query = """
56
+ mutation reportExpectation($id: ID!, $error: WorkErrorInput) {
57
+ workEdit(id: $id) {
58
+ reportExpectation(error: $error)
59
+ }
52
60
  }
53
- }
54
- """
55
- try:
56
- self.api.query(query, {"id": work_id, "error": error})
57
- except:
58
- self.api.app_logger.error("Cannot report expectation")
61
+ """
62
+ try:
63
+ self.api.query(query, {"id": work_id, "error": error})
64
+ except:
65
+ self.api.app_logger.error("Cannot report expectation")
59
66
 
60
67
  def add_expectations(self, work_id: str, expectations: int):
61
- self.api.app_logger.info(
62
- "Update action expectations",
63
- {"work_id": work_id, "expectations": expectations},
64
- )
65
- query = """
66
- mutation addExpectations($id: ID!, $expectations: Int) {
67
- workEdit(id: $id) {
68
- addExpectations(expectations: $expectations)
68
+ if self.api.bundle_send_to_queue:
69
+ self.api.app_logger.info(
70
+ "Update action expectations",
71
+ {"work_id": work_id, "expectations": expectations},
72
+ )
73
+ query = """
74
+ mutation addExpectations($id: ID!, $expectations: Int) {
75
+ workEdit(id: $id) {
76
+ addExpectations(expectations: $expectations)
77
+ }
69
78
  }
70
- }
71
- """
72
- try:
73
- self.api.query(query, {"id": work_id, "expectations": expectations})
74
- except:
75
- self.api.app_logger.error("Cannot report expectation")
79
+ """
80
+ try:
81
+ self.api.query(query, {"id": work_id, "expectations": expectations})
82
+ except:
83
+ self.api.app_logger.error("Cannot report expectation")
76
84
 
77
85
  def initiate_work(self, connector_id: str, friendly_name: str) -> str:
78
- self.api.app_logger.info("Initiate work", {"connector_id": connector_id})
79
- query = """
80
- mutation workAdd($connectorId: String!, $friendlyName: String) {
81
- workAdd(connectorId: $connectorId, friendlyName: $friendlyName) {
82
- id
86
+ if self.api.bundle_send_to_queue:
87
+ self.api.app_logger.info("Initiate work", {"connector_id": connector_id})
88
+ query = """
89
+ mutation workAdd($connectorId: String!, $friendlyName: String) {
90
+ workAdd(connectorId: $connectorId, friendlyName: $friendlyName) {
91
+ id
92
+ }
83
93
  }
84
- }
85
- """
86
- work = self.api.query(
87
- query, {"connectorId": connector_id, "friendlyName": friendly_name}
88
- )
89
- return work["data"]["workAdd"]["id"]
94
+ """
95
+ work = self.api.query(
96
+ query, {"connectorId": connector_id, "friendlyName": friendly_name}
97
+ )
98
+ return work["data"]["workAdd"]["id"]
90
99
 
91
100
  def delete_work(self, work_id: str):
92
101
  query = """
@@ -728,6 +728,32 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
728
728
  self.connect_auto = get_config_variable(
729
729
  "CONNECTOR_AUTO", ["connector", "auto"], config, False, False
730
730
  )
731
+ self.bundle_send_to_queue = get_config_variable(
732
+ "CONNECTOR_SEND_TO_QUEUE",
733
+ ["connector", "send_to_queue"],
734
+ config,
735
+ False,
736
+ True,
737
+ )
738
+ self.bundle_send_to_directory = get_config_variable(
739
+ "CONNECTOR_SEND_TO_DIRECTORY",
740
+ ["connector", "send_to_directory"],
741
+ config,
742
+ False,
743
+ False,
744
+ )
745
+ self.bundle_send_to_directory_path = get_config_variable(
746
+ "CONNECTOR_SEND_TO_DIRECTORY_PATH",
747
+ ["connector", "send_to_directory_path"],
748
+ config,
749
+ )
750
+ self.bundle_send_to_directory_retention = get_config_variable(
751
+ "CONNECTOR_SEND_TO_DIRECTORY_RETENTION",
752
+ ["connector", "send_to_directory_retention"],
753
+ config,
754
+ True,
755
+ 7,
756
+ )
731
757
  self.connect_only_contextual = get_config_variable(
732
758
  "CONNECTOR_ONLY_CONTEXTUAL",
733
759
  ["connector", "only_contextual"],
@@ -749,6 +775,8 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
749
775
  "CONNECTOR_VALIDATE_BEFORE_IMPORT",
750
776
  ["connector", "validate_before_import"],
751
777
  config,
778
+ False,
779
+ False,
752
780
  )
753
781
  # Start up the server to expose the metrics.
754
782
  expose_metrics = get_config_variable(
@@ -769,6 +797,7 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
769
797
  self.opencti_token,
770
798
  self.log_level,
771
799
  json_logging=self.opencti_json_logging,
800
+ bundle_send_to_queue=self.bundle_send_to_queue,
772
801
  )
773
802
  # - Impersonate API that will use applicant id
774
803
  # Behave like standard api if applicant not found
@@ -777,6 +806,7 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
777
806
  self.opencti_token,
778
807
  self.log_level,
779
808
  json_logging=self.opencti_json_logging,
809
+ bundle_send_to_queue=self.bundle_send_to_queue,
780
810
  )
781
811
  self.connector_logger = self.api.logger_class(self.connect_name)
782
812
  # For retro compatibility
@@ -1075,6 +1105,16 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
1075
1105
  bypass_validation = kwargs.get("bypass_validation", False)
1076
1106
  entity_id = kwargs.get("entity_id", None)
1077
1107
  file_name = kwargs.get("file_name", None)
1108
+ bundle_send_to_queue = kwargs.get("send_to_queue", self.bundle_send_to_queue)
1109
+ bundle_send_to_directory = kwargs.get(
1110
+ "send_to_directory", self.bundle_send_to_directory
1111
+ )
1112
+ bundle_send_to_directory_path = kwargs.get(
1113
+ "send_to_directory_path", self.bundle_send_to_directory_path
1114
+ )
1115
+ bundle_send_to_directory_retention = kwargs.get(
1116
+ "send_to_directory_retention", self.bundle_send_to_directory_retention
1117
+ )
1078
1118
 
1079
1119
  # In case of enrichment ingestion, ensure the sharing if needed
1080
1120
  if self.enrichment_shared_organizations is not None:
@@ -1114,13 +1154,14 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
1114
1154
  )
1115
1155
  bundle = json.dumps(bundle_data)
1116
1156
 
1157
+ # If execution in playbook, callback the api
1117
1158
  if self.playbook is not None:
1118
1159
  self.api.playbook.playbook_step_execution(self.playbook, bundle)
1119
1160
  return [bundle]
1120
1161
 
1162
+ # Upload workbench in case of pending validation
1121
1163
  if not file_name and work_id:
1122
1164
  file_name = f"{work_id}.json"
1123
-
1124
1165
  if self.connect_validate_before_import and not bypass_validation and file_name:
1125
1166
  self.api.upload_pending_file(
1126
1167
  file_name=file_name,
@@ -1130,8 +1171,61 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
1130
1171
  )
1131
1172
  return []
1132
1173
 
1133
- if entities_types is None:
1134
- entities_types = []
1174
+ # If directory setup, write the bundle to the target directory
1175
+ if bundle_send_to_directory and bundle_send_to_directory_path is not None:
1176
+ self.connector_logger.info(
1177
+ "The connector sending bundle to directory",
1178
+ {
1179
+ "connector": self.connect_name,
1180
+ "directory": bundle_send_to_directory_path,
1181
+ "also_queuing": bundle_send_to_queue,
1182
+ },
1183
+ )
1184
+ bundle_file = (
1185
+ self.connect_name.lower().replace(" ", "_")
1186
+ + "-"
1187
+ + time.strftime("%Y%m%d-%H%M%S-")
1188
+ + str(time.time())
1189
+ + ".json"
1190
+ )
1191
+ write_file = os.path.join(
1192
+ bundle_send_to_directory_path, bundle_file + ".tmp"
1193
+ )
1194
+ message_bundle = {
1195
+ "bundle_type": "DIRECTORY_BUNDLE",
1196
+ "applicant_id": self.applicant_id,
1197
+ "connector": {
1198
+ "id": self.connect_id,
1199
+ "name": self.connect_name,
1200
+ "type": self.connect_type,
1201
+ "scope": self.connect_scope,
1202
+ "auto": self.connect_auto,
1203
+ "validate_before_import": self.connect_validate_before_import,
1204
+ },
1205
+ "entities_types": entities_types,
1206
+ "bundle": json.loads(bundle),
1207
+ "update": update,
1208
+ }
1209
+ # Maintains the list of files under control
1210
+ if bundle_send_to_directory_retention > 0: # If 0, disable the auto remove
1211
+ current_time = time.time()
1212
+ for f in os.listdir(bundle_send_to_directory_path):
1213
+ if f.endswith(".json"):
1214
+ file_location = os.path.join(bundle_send_to_directory_path, f)
1215
+ file_time = os.stat(file_location).st_mtime
1216
+ is_expired_file = (
1217
+ file_time
1218
+ < current_time - 86400 * bundle_send_to_directory_retention
1219
+ ) # 86400 = 1 day
1220
+ if is_expired_file:
1221
+ os.remove(file_location)
1222
+ # Write the bundle to target directory
1223
+ with open(write_file, "w") as f:
1224
+ str_bundle = json.dumps(message_bundle)
1225
+ f.write(str_bundle)
1226
+ # Rename the file after full write
1227
+ final_write_file = os.path.join(bundle_send_to_directory_path, bundle_file)
1228
+ os.rename(write_file, final_write_file)
1135
1229
 
1136
1230
  if bypass_split:
1137
1231
  bundles = [bundle]
@@ -1143,44 +1237,48 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
1143
1237
  self.metric.inc("error_count")
1144
1238
  raise ValueError("Nothing to import")
1145
1239
 
1146
- if work_id:
1147
- self.api.work.add_expectations(work_id, len(bundles))
1148
-
1149
- pika_credentials = pika.PlainCredentials(
1150
- self.connector_config["connection"]["user"],
1151
- self.connector_config["connection"]["pass"],
1152
- )
1153
- pika_parameters = pika.ConnectionParameters(
1154
- host=self.connector_config["connection"]["host"],
1155
- port=self.connector_config["connection"]["port"],
1156
- virtual_host=self.connector_config["connection"]["vhost"],
1157
- credentials=pika_credentials,
1158
- ssl_options=(
1159
- pika.SSLOptions(
1160
- create_mq_ssl_context(self.config),
1161
- self.connector_config["connection"]["host"],
1162
- )
1163
- if self.connector_config["connection"]["use_ssl"]
1164
- else None
1165
- ),
1166
- )
1167
- pika_connection = pika.BlockingConnection(pika_parameters)
1168
- channel = pika_connection.channel()
1169
- try:
1170
- channel.confirm_delivery()
1171
- except Exception as err: # pylint: disable=broad-except
1172
- self.connector_logger.warning(str(err))
1173
- for sequence, bundle in enumerate(bundles, start=1):
1174
- self._send_bundle(
1175
- channel,
1176
- bundle,
1177
- work_id=work_id,
1178
- entities_types=entities_types,
1179
- sequence=sequence,
1180
- update=update,
1240
+ if bundle_send_to_queue:
1241
+ if work_id:
1242
+ self.api.work.add_expectations(work_id, len(bundles))
1243
+ if entities_types is None:
1244
+ entities_types = []
1245
+ pika_credentials = pika.PlainCredentials(
1246
+ self.connector_config["connection"]["user"],
1247
+ self.connector_config["connection"]["pass"],
1248
+ )
1249
+ pika_parameters = pika.ConnectionParameters(
1250
+ host=self.connector_config["connection"]["host"],
1251
+ port=self.connector_config["connection"]["port"],
1252
+ virtual_host=self.connector_config["connection"]["vhost"],
1253
+ credentials=pika_credentials,
1254
+ ssl_options=(
1255
+ pika.SSLOptions(
1256
+ create_mq_ssl_context(self.config),
1257
+ self.connector_config["connection"]["host"],
1258
+ )
1259
+ if self.connector_config["connection"]["use_ssl"]
1260
+ else None
1261
+ ),
1181
1262
  )
1182
- channel.close()
1183
- pika_connection.close()
1263
+ pika_connection = pika.BlockingConnection(pika_parameters)
1264
+ channel = pika_connection.channel()
1265
+ try:
1266
+ channel.confirm_delivery()
1267
+ except Exception as err: # pylint: disable=broad-except
1268
+ self.connector_logger.warning(str(err))
1269
+ self.connector_logger.info(self.connect_name + " sending bundle to queue")
1270
+ for sequence, bundle in enumerate(bundles, start=1):
1271
+ self._send_bundle(
1272
+ channel,
1273
+ bundle,
1274
+ work_id=work_id,
1275
+ entities_types=entities_types,
1276
+ sequence=sequence,
1277
+ update=update,
1278
+ )
1279
+ channel.close()
1280
+ pika_connection.close()
1281
+
1184
1282
  return bundles
1185
1283
 
1186
1284
  def _send_bundle(self, channel, bundle, **kwargs) -> None:
@@ -1212,6 +1310,7 @@ class OpenCTIConnectorHelper: # pylint: disable=too-many-public-methods
1212
1310
  # if self.current_work_id is None:
1213
1311
  # raise ValueError('The job id must be specified')
1214
1312
  message = {
1313
+ "bundle_type": "QUEUE_BUNDLE",
1215
1314
  "applicant_id": self.applicant_id,
1216
1315
  "action_sequence": sequence,
1217
1316
  "entities_types": entities_types,
@@ -458,6 +458,10 @@ class Incident:
458
458
  stix_object["x_opencti_granted_refs"] = (
459
459
  self.opencti.get_attribute_in_extension("granted_refs", stix_object)
460
460
  )
461
+ if "x_opencti_workflow_id" not in stix_object:
462
+ stix_object["x_opencti_workflow_id"] = (
463
+ self.opencti.get_attribute_in_extension("workflow_id", stix_object)
464
+ )
461
465
 
462
466
  return self.create(
463
467
  stix_id=stix_object["id"],
@@ -468,6 +468,7 @@ class Indicator:
468
468
  x_opencti_stix_ids = kwargs.get("x_opencti_stix_ids", None)
469
469
  create_observables = kwargs.get("x_opencti_create_observables", False)
470
470
  granted_refs = kwargs.get("objectOrganization", None)
471
+ x_opencti_workflow_id = kwargs.get("x_opencti_workflow_id", None)
471
472
  update = kwargs.get("update", False)
472
473
 
473
474
  if (
@@ -528,6 +529,7 @@ class Indicator:
528
529
  "x_opencti_stix_ids": x_opencti_stix_ids,
529
530
  "killChainPhases": kill_chain_phases,
530
531
  "createObservables": create_observables,
532
+ "x_opencti_workflow_id": x_opencti_workflow_id,
531
533
  "update": update,
532
534
  }
533
535
  },
@@ -642,6 +644,10 @@ class Indicator:
642
644
  stix_object["x_opencti_granted_refs"] = (
643
645
  self.opencti.get_attribute_in_extension("granted_refs", stix_object)
644
646
  )
647
+ if "x_opencti_workflow_id" not in stix_object:
648
+ stix_object["x_opencti_workflow_id"] = (
649
+ self.opencti.get_attribute_in_extension("workflow_id", stix_object)
650
+ )
645
651
 
646
652
  return self.create(
647
653
  stix_id=stix_object["id"],
@@ -740,6 +746,11 @@ class Indicator:
740
746
  if "x_opencti_granted_refs" in stix_object
741
747
  else None
742
748
  ),
749
+ x_opencti_workflow_id=(
750
+ stix_object["x_opencti_workflow_id"]
751
+ if "x_opencti_workflow_id" in stix_object
752
+ else None
753
+ ),
743
754
  update=update,
744
755
  )
745
756
  else:
@@ -363,6 +363,7 @@ class IntrusionSet:
363
363
  secondary_motivations = kwargs.get("secondary_motivations", None)
364
364
  x_opencti_stix_ids = kwargs.get("x_opencti_stix_ids", None)
365
365
  granted_refs = kwargs.get("objectOrganization", None)
366
+ x_opencti_workflow_id = kwargs.get("x_opencti_workflow_id", None)
366
367
  update = kwargs.get("update", False)
367
368
 
368
369
  if name is not None:
@@ -402,6 +403,7 @@ class IntrusionSet:
402
403
  "primary_motivation": primary_motivation,
403
404
  "secondary_motivations": secondary_motivations,
404
405
  "x_opencti_stix_ids": x_opencti_stix_ids,
406
+ "x_opencti_workflow_id": x_opencti_workflow_id,
405
407
  "update": update,
406
408
  }
407
409
  },
@@ -435,7 +437,11 @@ class IntrusionSet:
435
437
  stix_object["x_opencti_granted_refs"] = (
436
438
  self.opencti.get_attribute_in_extension("granted_refs", stix_object)
437
439
  )
438
-
440
+ if "x_opencti_workflow_id" not in stix_object:
441
+ stix_object["x_opencti_workflow_id"] = (
442
+ self.opencti.get_attribute_in_extension("workflow_id", stix_object)
443
+ )
444
+ 7
439
445
  return self.create(
440
446
  stix_id=stix_object["id"],
441
447
  createdBy=(
@@ -500,6 +506,11 @@ class IntrusionSet:
500
506
  if "x_opencti_granted_refs" in stix_object
501
507
  else None
502
508
  ),
509
+ x_opencti_workflow_id=(
510
+ stix_object["x_opencti_workflow_id"]
511
+ if "x_opencti_workflow_id" in stix_object
512
+ else None
513
+ ),
503
514
  update=update,
504
515
  )
505
516
  else:
@@ -401,6 +401,7 @@ class MalwareAnalysis:
401
401
  analysisSco = kwargs.get("analysisSco", None)
402
402
  x_opencti_stix_ids = kwargs.get("x_opencti_stix_ids", None)
403
403
  granted_refs = kwargs.get("objectOrganization", None)
404
+ x_opencti_workflow_id = kwargs.get("x_opencti_workflow_id", None)
404
405
  update = kwargs.get("update", False)
405
406
 
406
407
  if product is not None and result_name is not None:
@@ -449,6 +450,7 @@ class MalwareAnalysis:
449
450
  "analysisSample": sample,
450
451
  "analysisSco": analysisSco,
451
452
  "x_opencti_stix_ids": x_opencti_stix_ids,
453
+ "x_opencti_workflow_id": x_opencti_workflow_id,
452
454
  "update": update,
453
455
  }
454
456
  },
@@ -482,6 +484,10 @@ class MalwareAnalysis:
482
484
  stix_object["x_opencti_granted_refs"] = (
483
485
  self.opencti.get_attribute_in_extension("granted_refs", stix_object)
484
486
  )
487
+ if "x_opencti_workflow_id" not in stix_object:
488
+ stix_object["x_opencti_workflow_id"] = (
489
+ self.opencti.get_attribute_in_extension("workflow_id", stix_object)
490
+ )
485
491
 
486
492
  return self.create(
487
493
  stix_id=stix_object["id"],
@@ -574,6 +580,11 @@ class MalwareAnalysis:
574
580
  if "x_opencti_granted_refs" in stix_object
575
581
  else None
576
582
  ),
583
+ x_opencti_workflow_id=(
584
+ stix_object["x_opencti_workflow_id"]
585
+ if "x_opencti_workflow_id" in stix_object
586
+ else None
587
+ ),
577
588
  update=update,
578
589
  )
579
590
  else:
@@ -615,6 +615,12 @@ class StixCoreObject:
615
615
  ... on PhoneNumber {
616
616
  value
617
617
  }
618
+ ... on TrackingNumber {
619
+ value
620
+ }
621
+ ... on Credential {
622
+ value
623
+ }
618
624
  ... on PaymentCard {
619
625
  card_number
620
626
  expiration_date
@@ -282,6 +282,12 @@ class StixCyberObservable:
282
282
  ... on PhoneNumber {
283
283
  value
284
284
  }
285
+ ... on TrackingNumber {
286
+ value
287
+ }
288
+ ... on Credential {
289
+ value
290
+ }
285
291
  ... on PaymentCard {
286
292
  card_number
287
293
  expiration_date
@@ -576,6 +582,12 @@ class StixCyberObservable:
576
582
  ... on PhoneNumber {
577
583
  value
578
584
  }
585
+ ... on TrackingNumber {
586
+ value
587
+ }
588
+ ... on Credential {
589
+ value
590
+ }
579
591
  ... on PaymentCard {
580
592
  card_number
581
593
  expiration_date
@@ -857,6 +869,15 @@ class StixCyberObservable:
857
869
  type = "IPv6-Addr"
858
870
  elif type.lower() == "hostname" or type.lower() == "x-opencti-hostname":
859
871
  type = "Hostname"
872
+ elif type.lower() == "payment-card" or type.lower() == "x-opencti-payment-card":
873
+ type = "Payment-Card"
874
+ elif type.lower() == "credential" or type.lower() == "x-opencti-credential":
875
+ type = "Credential"
876
+ elif (
877
+ type.lower() == "tracking-number"
878
+ or type.lower() == "x-opencti-tracking-number"
879
+ ):
880
+ type = "Tracking-Number"
860
881
  elif (
861
882
  type.lower() == "cryptocurrency-wallet"
862
883
  or type.lower() == "x-opencti-cryptocurrency-wallet"
@@ -974,6 +995,8 @@ class StixCyberObservable:
974
995
  $UserAgent: UserAgentAddInput
975
996
  $BankAccount: BankAccountAddInput
976
997
  $PhoneNumber: PhoneNumberAddInput
998
+ $Credential: CredentialAddInput
999
+ $TrackingNumber: TrackingNumberAddInput
977
1000
  $PaymentCard: PaymentCardAddInput
978
1001
  $MediaContent: MediaContentAddInput
979
1002
  ) {
@@ -1016,6 +1039,8 @@ class StixCyberObservable:
1016
1039
  UserAgent: $UserAgent
1017
1040
  BankAccount: $BankAccount
1018
1041
  PhoneNumber: $PhoneNumber
1042
+ Credential: $Credential
1043
+ TrackingNumber: $TrackingNumber
1019
1044
  PaymentCard: $PaymentCard
1020
1045
  MediaContent: $MediaContent
1021
1046
  ) {
@@ -1508,15 +1533,6 @@ class StixCyberObservable:
1508
1533
  observable_data["value"] if "value" in observable_data else None
1509
1534
  ),
1510
1535
  }
1511
- elif (
1512
- type == "Cryptocurrency-Wallet"
1513
- or type == "X-OpenCTI-Cryptocurrency-Wallet"
1514
- ):
1515
- input_variables["CryptocurrencyWallet"] = {
1516
- "value": (
1517
- observable_data["value"] if "value" in observable_data else None
1518
- ),
1519
- }
1520
1536
  elif type == "Hostname":
1521
1537
  input_variables["Hostname"] = {
1522
1538
  "value": (
@@ -1588,8 +1604,50 @@ class StixCyberObservable:
1588
1604
  else None
1589
1605
  ),
1590
1606
  }
1607
+ elif type == "Payment-Card" or type.lower() == "x-opencti-payment-card":
1608
+ input_variables["PaymentCard"] = {
1609
+ "card_number": (
1610
+ observable_data["card_number"]
1611
+ if "card_number" in observable_data
1612
+ else None
1613
+ ),
1614
+ "expiration_date": (
1615
+ observable_data["expiration_date"]
1616
+ if "expiration_date" in observable_data
1617
+ else None
1618
+ ),
1619
+ "cvv": observable_data["cvv"] if "cvv" in observable_data else None,
1620
+ "holder_name": (
1621
+ observable_data["holder_name"]
1622
+ if "holder_name" in observable_data
1623
+ else None
1624
+ ),
1625
+ }
1626
+ elif (
1627
+ type == "Cryptocurrency-Wallet"
1628
+ or type.lower() == "x-opencti-cryptocurrency-wallet"
1629
+ ):
1630
+ input_variables["CryptocurrencyWallet"] = {
1631
+ "value": (
1632
+ observable_data["value"] if "value" in observable_data else None
1633
+ ),
1634
+ }
1635
+ elif type == "Credential" or type.lower() == "x-opencti-credential":
1636
+ input_variables["Credential"] = {
1637
+ "value": (
1638
+ observable_data["value"] if "value" in observable_data else None
1639
+ ),
1640
+ }
1641
+ elif (
1642
+ type == "Tracking-Number" or type.lower() == "x-opencti-tracking-number"
1643
+ ):
1644
+ input_variables["TrackingNumber"] = {
1645
+ "value": (
1646
+ observable_data["value"] if "value" in observable_data else None
1647
+ ),
1648
+ }
1591
1649
  result = self.opencti.query(query, input_variables)
1592
- if "payload_bin" in observable_data and "mime/type" in observable_data:
1650
+ if "payload_bin" in observable_data and "mime_type" in observable_data:
1593
1651
  self.add_file(
1594
1652
  id=result["data"]["stixCyberObservableAdd"]["id"],
1595
1653
  file_name=(
@@ -42,6 +42,8 @@ class StixCyberObservableTypes(Enum):
42
42
  USER_AGENT = "User-Agent"
43
43
  BANK_ACCOUNT = "Bank-Account"
44
44
  PHONE_NUMBER = "Phone-Number"
45
+ CREDENTIAL = "Credential"
46
+ TRACKING_NUMBER = "Tracking-Number"
45
47
  PAYMENT_CARD = "Payment-Card"
46
48
  MEDIA_CONTENT = "Media-Content"
47
49
  SIMPLE_OBSERVABLE = "Simple-Observable"
@@ -66,6 +68,7 @@ class IdentityTypes(Enum):
66
68
 
67
69
  class ThreatActorTypes(Enum):
68
70
  THREAT_ACTOR_GROUP = "Threat-Actor-Group"
71
+ THREAT_ACTOR_INDIVIDUAL = "Threat-Actor-Individual"
69
72
 
70
73
  @classmethod
71
74
  def has_value(cls, value):
@@ -263,6 +266,73 @@ class CustomObservableText:
263
266
  pass
264
267
 
265
268
 
269
+ @CustomObservable(
270
+ "payment-card",
271
+ [
272
+ ("value", StringProperty(required=True)),
273
+ ("card_number", StringProperty(required=True)),
274
+ ("expiration_date", StringProperty(required=False)),
275
+ ("cvv", StringProperty(required=False)),
276
+ ("holder_name", StringProperty(required=False)),
277
+ ("spec_version", StringProperty(fixed="2.1")),
278
+ (
279
+ "object_marking_refs",
280
+ ListProperty(
281
+ ReferenceProperty(valid_types="marking-definition", spec_version="2.1")
282
+ ),
283
+ ),
284
+ ],
285
+ ["card_number"],
286
+ )
287
+ class CustomObservablePaymentCard:
288
+ """Payment card observable."""
289
+
290
+ pass
291
+
292
+
293
+ @CustomObservable(
294
+ "bank-account",
295
+ [
296
+ ("value", StringProperty(required=True)),
297
+ ("iban", StringProperty(required=True)),
298
+ ("bic", StringProperty(required=False)),
299
+ ("account_number", StringProperty(required=False)),
300
+ ("spec_version", StringProperty(fixed="2.1")),
301
+ (
302
+ "object_marking_refs",
303
+ ListProperty(
304
+ ReferenceProperty(valid_types="marking-definition", spec_version="2.1")
305
+ ),
306
+ ),
307
+ ],
308
+ ["iban"],
309
+ )
310
+ class CustomObservableBankAccount:
311
+ """Bank Account observable."""
312
+
313
+ pass
314
+
315
+
316
+ @CustomObservable(
317
+ "credential",
318
+ [
319
+ ("value", StringProperty(required=True)),
320
+ ("spec_version", StringProperty(fixed="2.1")),
321
+ (
322
+ "object_marking_refs",
323
+ ListProperty(
324
+ ReferenceProperty(valid_types="marking-definition", spec_version="2.1")
325
+ ),
326
+ ),
327
+ ],
328
+ ["value"],
329
+ )
330
+ class CustomObservableCredential:
331
+ """Credential observable."""
332
+
333
+ pass
334
+
335
+
266
336
  @CustomObservable(
267
337
  "cryptocurrency-wallet",
268
338
  [
@@ -283,6 +353,46 @@ class CustomObservableCryptocurrencyWallet:
283
353
  pass
284
354
 
285
355
 
356
+ @CustomObservable(
357
+ "phone-number",
358
+ [
359
+ ("value", StringProperty(required=True)),
360
+ ("spec_version", StringProperty(fixed="2.1")),
361
+ (
362
+ "object_marking_refs",
363
+ ListProperty(
364
+ ReferenceProperty(valid_types="marking-definition", spec_version="2.1")
365
+ ),
366
+ ),
367
+ ],
368
+ ["value"],
369
+ )
370
+ class CustomObservablePhoneNumber:
371
+ """Phone number observable."""
372
+
373
+ pass
374
+
375
+
376
+ @CustomObservable(
377
+ "tracking-number",
378
+ [
379
+ ("value", StringProperty(required=True)),
380
+ ("spec_version", StringProperty(fixed="2.1")),
381
+ (
382
+ "object_marking_refs",
383
+ ListProperty(
384
+ ReferenceProperty(valid_types="marking-definition", spec_version="2.1")
385
+ ),
386
+ ),
387
+ ],
388
+ ["value"],
389
+ )
390
+ class CustomObservableTrackingNumber:
391
+ """Tracking number observable."""
392
+
393
+ pass
394
+
395
+
286
396
  @CustomObservable(
287
397
  "user-agent",
288
398
  [
@@ -835,6 +835,21 @@ class OpenCTIStix2:
835
835
  "Vulnerability": self.opencti.vulnerability.read,
836
836
  }
837
837
 
838
+ def get_reader(self, entity_type: str):
839
+ # Map types
840
+ if entity_type == "StixFile":
841
+ entity_type = "File"
842
+ if IdentityTypes.has_value(entity_type):
843
+ entity_type = "Identity"
844
+ if LocationTypes.has_value(entity_type):
845
+ entity_type = "Location"
846
+ if StixCyberObservableTypes.has_value(entity_type):
847
+ entity_type = "Stix-Cyber-Observable"
848
+ readers = self.get_readers()
849
+ return readers.get(
850
+ entity_type, lambda **kwargs: self.unknown_type({"type": entity_type})
851
+ )
852
+
838
853
  # endregion
839
854
 
840
855
  # region import
@@ -7,8 +7,11 @@ STIX_CYBER_OBSERVABLE_MAPPING = {
7
7
  "directory": "Directory",
8
8
  "domain-name": "Domain-Name",
9
9
  "email-addr": "Email-Addr",
10
- "file": "StixFile",
11
10
  "email-message": "Email-Message",
11
+ "email-mime-part-type": "Email-Mime-Part-Type",
12
+ "artifact": "Artifact",
13
+ "file": "StixFile",
14
+ "x509-certificate": "X509-Certificate",
12
15
  "ipv4-addr": "IPv4-Addr",
13
16
  "ipv6-addr": "IPv6-Addr",
14
17
  "mac-addr": "Mac-Addr",
@@ -21,8 +24,14 @@ STIX_CYBER_OBSERVABLE_MAPPING = {
21
24
  "windows-registry-key": "Windows-Registry-Key",
22
25
  "windows-registry-value-type": "Windows-Registry-Value-Type",
23
26
  "hostname": "Hostname",
27
+ "cryptographic-key": "Cryptographic-Key",
28
+ "cryptocurrency-wallet": "Cryptocurrency-Wallet",
29
+ "text": "Text",
30
+ "user-agent": "User-Agent",
24
31
  "bank-account": "Bank-Account",
25
32
  "phone-number": "Phone-Number",
33
+ "credential": "Credential",
34
+ "tracking-number": "Tracking-Number",
26
35
  "payment-card": "Payment-Card",
27
36
  "media-content": "Media-Content",
28
37
  }
@@ -54,6 +63,8 @@ PATTERN_MAPPING = {
54
63
  "Bank-Account": ["iban"],
55
64
  "Phone-Number": ["value"],
56
65
  "Payment-Card": ["card_number"],
66
+ "Tracking-Number": ["value"],
67
+ "Credential": ["value"],
57
68
  "Media-Content": ["url"],
58
69
  }
59
70
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pycti
3
- Version: 6.0.9
3
+ Version: 6.0.10
4
4
  Summary: Python API client for OpenCTI.
5
5
  Home-page: https://github.com/OpenCTI-Platform/client-python
6
6
  Author: Filigran
@@ -29,12 +29,12 @@ Requires-Dist: python-magic-bin~=0.4.14; sys_platform == "win32"
29
29
  Requires-Dist: python_json_logger~=2.0.4
30
30
  Requires-Dist: pyyaml~=6.0
31
31
  Requires-Dist: requests~=2.31.0
32
- Requires-Dist: setuptools~=69.2.0
32
+ Requires-Dist: setuptools~=69.5.1
33
33
  Requires-Dist: filigran-sseclient~=1.0.0
34
34
  Requires-Dist: stix2~=3.0.1
35
35
  Requires-Dist: cachetools~=5.3.0
36
36
  Provides-Extra: dev
37
- Requires-Dist: black~=24.3.0; extra == "dev"
37
+ Requires-Dist: black~=24.4.0; extra == "dev"
38
38
  Requires-Dist: build~=1.2.1; extra == "dev"
39
39
  Requires-Dist: isort~=5.13.0; extra == "dev"
40
40
  Requires-Dist: types-pytz~=2024.1.0.20240203; extra == "dev"
@@ -47,7 +47,7 @@ Requires-Dist: types-python-dateutil~=2.9.0; extra == "dev"
47
47
  Requires-Dist: wheel~=0.43.0; extra == "dev"
48
48
  Provides-Extra: doc
49
49
  Requires-Dist: autoapi~=2.0.1; extra == "doc"
50
- Requires-Dist: sphinx-autodoc-typehints~=2.0.0; extra == "doc"
50
+ Requires-Dist: sphinx-autodoc-typehints~=2.1.0; extra == "doc"
51
51
  Requires-Dist: sphinx-rtd-theme~=2.0.0; extra == "doc"
52
52
 
53
53
  # OpenCTI client for Python
@@ -4,7 +4,7 @@ prometheus-client~=0.20.0
4
4
  python_json_logger~=2.0.4
5
5
  pyyaml~=6.0
6
6
  requests~=2.31.0
7
- setuptools~=69.2.0
7
+ setuptools~=69.5.1
8
8
  filigran-sseclient~=1.0.0
9
9
  stix2~=3.0.1
10
10
  cachetools~=5.3.0
@@ -16,7 +16,7 @@ python-magic~=0.4.27
16
16
  python-magic-bin~=0.4.14
17
17
 
18
18
  [dev]
19
- black~=24.3.0
19
+ black~=24.4.0
20
20
  build~=1.2.1
21
21
  isort~=5.13.0
22
22
  types-pytz~=2024.1.0.20240203
@@ -30,5 +30,5 @@ wheel~=0.43.0
30
30
 
31
31
  [doc]
32
32
  autoapi~=2.0.1
33
- sphinx-autodoc-typehints~=2.0.0
33
+ sphinx-autodoc-typehints~=2.1.0
34
34
  sphinx-rtd-theme~=2.0.0
@@ -40,14 +40,14 @@ install_requires =
40
40
  python_json_logger~=2.0.4
41
41
  pyyaml~=6.0
42
42
  requests~=2.31.0
43
- setuptools~=69.2.0
43
+ setuptools~=69.5.1
44
44
  filigran-sseclient~=1.0.0
45
45
  stix2~=3.0.1
46
46
  cachetools~=5.3.0
47
47
 
48
48
  [options.extras_require]
49
49
  dev =
50
- black~=24.3.0
50
+ black~=24.4.0
51
51
  build~=1.2.1
52
52
  isort~=5.13.0
53
53
  types-pytz~=2024.1.0.20240203
@@ -60,7 +60,7 @@ dev =
60
60
  wheel~=0.43.0
61
61
  doc =
62
62
  autoapi~=2.0.1
63
- sphinx-autodoc-typehints~=2.0.0
63
+ sphinx-autodoc-typehints~=2.1.0
64
64
  sphinx-rtd-theme~=2.0.0
65
65
 
66
66
  [egg_info]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes