diff --git a/pycti/connector/opencti_connector_helper.py b/pycti/connector/opencti_connector_helper.py index 92c0a00d..6d843729 100644 --- a/pycti/connector/opencti_connector_helper.py +++ b/pycti/connector/opencti_connector_helper.py @@ -37,10 +37,21 @@ def killProgramHook(etype, value, tb): + """Exception hook to terminate the program. + + :param etype: Exception type + :param value: Exception value + :param tb: Traceback object + """ os.kill(os.getpid(), signal.SIGTERM) def start_loop(loop): + """Start an asyncio event loop. + + :param loop: The asyncio event loop to start + :type loop: asyncio.AbstractEventLoop + """ asyncio.set_event_loop(loop) loop.run_forever() @@ -93,10 +104,24 @@ def get_config_variable( def is_memory_certificate(certificate): + """Check if a certificate is provided as a PEM string in memory. + + :param certificate: The certificate data to check + :type certificate: str + :return: True if the certificate is a PEM string, False otherwise + :rtype: bool + """ return certificate.startswith("-----BEGIN") def ssl_verify_locations(ssl_context, certdata): + """Load certificate verification locations into SSL context. + + :param ssl_context: The SSL context to configure + :type ssl_context: ssl.SSLContext + :param certdata: Certificate data (file path or PEM string) + :type certdata: str or None + """ if certdata is None: return @@ -106,9 +131,17 @@ def ssl_verify_locations(ssl_context, certdata): ssl_context.load_verify_locations(cafile=certdata) -# As cert must be written in files to be loaded in ssl context -# Creates a temporary file in the most secure manner possible def data_to_temp_file(data): + """Write data to a temporary file securely. + + Creates a temporary file in the most secure manner possible. + The file is readable and writable only by the creating user ID. + + :param data: The data to write to the temporary file + :type data: str + :return: Path to the created temporary file + :rtype: str + """ # The file is readable and writable only by the creating user ID. # If the operating system uses permission bits to indicate whether a # file is executable, the file is executable by no one. The file @@ -121,6 +154,17 @@ def data_to_temp_file(data): def ssl_cert_chain(ssl_context, cert_data, key_data, passphrase): + """Load certificate chain into SSL context. + + :param ssl_context: The SSL context to configure + :type ssl_context: ssl.SSLContext + :param cert_data: Certificate data (file path or PEM string) + :type cert_data: str or None + :param key_data: Private key data (file path or PEM string) + :type key_data: str or None + :param passphrase: Passphrase for the private key + :type passphrase: str or None + """ if cert_data is None: return @@ -147,6 +191,13 @@ def ssl_cert_chain(ssl_context, cert_data, key_data, passphrase): def create_callback_ssl_context(config) -> ssl.SSLContext: + """Create SSL context for API callback server. + + :param config: Configuration dictionary + :type config: dict + :return: Configured SSL context + :rtype: ssl.SSLContext + """ listen_protocol_api_ssl_key = get_config_variable( "LISTEN_PROTOCOL_API_SSL_KEY", ["connector", "listen_protocol_api_ssl_key"], @@ -176,6 +227,13 @@ def create_callback_ssl_context(config) -> ssl.SSLContext: def create_mq_ssl_context(config) -> ssl.SSLContext: + """Create SSL context for message queue connections. + + :param config: Configuration dictionary + :type config: dict + :return: Configured SSL context for MQ connections + :rtype: ssl.SSLContext + """ use_ssl_ca = get_config_variable("MQ_USE_SSL_CA", ["mq", "use_ssl_ca"], config) use_ssl_cert = get_config_variable( "MQ_USE_SSL_CERT", ["mq", "use_ssl_cert"], config @@ -292,6 +350,11 @@ def _process_message(self, channel, method, properties, body) -> None: ) def _set_draft_id(self, draft_id): + """Set the draft ID for the helper and API instances. + + :param draft_id: The draft ID to set + :type draft_id: str + """ self.helper.draft_id = draft_id self.helper.api.set_draft_id(draft_id) self.helper.api_impersonate.set_draft_id(draft_id) @@ -546,6 +609,11 @@ def run(self) -> None: raise ValueError("Unsupported listen protocol type") def stop(self): + """Stop the ListenQueue thread and close connections. + + This method sets the exit event, closes the RabbitMQ connection, + and waits for the processing thread to complete. + """ self.helper.connector_logger.info("Preparing ListenQueue for clean shutdown") self.exit_event.set() self.pika_connection.close() @@ -794,6 +862,10 @@ def run(self) -> None: # pylint: disable=too-many-branches sys.excepthook(*sys.exc_info()) def stop(self): + """Stop the ListenStream thread. + + This method sets the exit event to signal the stream listening thread to stop. + """ self.helper.connector_logger.info("Preparing ListenStream for clean shutdown") self.exit_event.set() @@ -817,6 +889,11 @@ def __init__( @property def all_details(self): + """Get all connector information details as a dictionary. + + :return: Dictionary containing all connector status information + :rtype: dict + """ return { "run_and_terminate": self._run_and_terminate, "buffering": self._buffering, @@ -832,6 +909,11 @@ def run_and_terminate(self) -> bool: @run_and_terminate.setter def run_and_terminate(self, value): + """Set the run_and_terminate flag. + + :param value: Whether the connector should run once and terminate + :type value: bool + """ self._run_and_terminate = value @property @@ -840,6 +922,11 @@ def buffering(self) -> bool: @buffering.setter def buffering(self, value): + """Set the buffering status. + + :param value: Whether the connector is currently buffering + :type value: bool + """ self._buffering = value @property @@ -848,6 +935,11 @@ def queue_threshold(self) -> float: @queue_threshold.setter def queue_threshold(self, value): + """Set the queue threshold value. + + :param value: The queue size threshold in MB + :type value: float + """ self._queue_threshold = value @property @@ -856,6 +948,11 @@ def queue_messages_size(self) -> float: @queue_messages_size.setter def queue_messages_size(self, value): + """Set the current queue messages size. + + :param value: The current size of messages in the queue in MB + :type value: float + """ self._queue_messages_size = value @property @@ -864,6 +961,11 @@ def next_run_datetime(self) -> datetime: @next_run_datetime.setter def next_run_datetime(self, value): + """Set the next scheduled run datetime. + + :param value: The datetime for the next scheduled run + :type value: datetime + """ self._next_run_datetime = value @property @@ -872,6 +974,11 @@ def last_run_datetime(self) -> datetime: @last_run_datetime.setter def last_run_datetime(self, value): + """Set the last run datetime. + + :param value: The datetime of the last run + :type value: datetime + """ self._last_run_datetime = value @@ -1244,6 +1351,11 @@ def __init__(self, config: Dict, playbook_compatible=False) -> None: self.listen_queue = None def stop(self) -> None: + """Stop the connector and clean up resources. + + This method stops all running threads (listen queue, ping thread) and + unregisters the connector from OpenCTI. + """ self.connector_logger.info("Preparing connector for clean shutdown") if self.listen_queue: self.listen_queue.stop() @@ -1253,9 +1365,20 @@ def stop(self) -> None: self.api.connector.unregister(self.connector_id) def get_name(self) -> Optional[Union[bool, int, str]]: + """Get the connector name. + + :return: The name of the connector + :rtype: Optional[Union[bool, int, str]] + """ return self.connect_name def get_stream_collection(self): + """Get the stream collection configuration. + + :return: Stream collection configuration dictionary + :rtype: dict + :raises ValueError: If no stream is connected + """ if self.connect_live_stream_id is not None: if self.connect_live_stream_id in ["live", "raw"]: return { @@ -1290,12 +1413,27 @@ def get_stream_collection(self): raise ValueError("This connector is not connected to any stream") def get_only_contextual(self) -> Optional[Union[bool, int, str]]: + """Get the only_contextual configuration value. + + :return: Whether the connector processes only contextual data + :rtype: Optional[Union[bool, int, str]] + """ return self.connect_only_contextual def get_run_and_terminate(self) -> Optional[Union[bool, int, str]]: + """Get the run_and_terminate configuration value. + + :return: Whether the connector should run once and terminate + :rtype: Optional[Union[bool, int, str]] + """ return self.connect_run_and_terminate def get_validate_before_import(self) -> Optional[Union[bool, int, str]]: + """Get the validate_before_import configuration value. + + :return: Whether to validate data before importing + :rtype: Optional[Union[bool, int, str]] + """ return self.connect_validate_before_import def set_state(self, state) -> None: @@ -1326,6 +1464,11 @@ def get_state(self) -> Optional[Dict]: return None def force_ping(self): + """Force a ping to the OpenCTI API to update connector state. + + This method manually triggers a ping to synchronize the connector state + with the OpenCTI platform. + """ try: initial_state = self.get_state() connector_info = self.connector_info.all_details @@ -1738,12 +1881,27 @@ def listen_stream( return self.listen_stream def get_opencti_url(self) -> Optional[Union[bool, int, str]]: + """Get the OpenCTI URL. + + :return: The URL of the OpenCTI platform + :rtype: Optional[Union[bool, int, str]] + """ return self.opencti_url def get_opencti_token(self) -> Optional[Union[bool, int, str]]: + """Get the OpenCTI API token. + + :return: The API token for OpenCTI authentication + :rtype: Optional[Union[bool, int, str]] + """ return self.opencti_token def get_connector(self) -> OpenCTIConnector: + """Get the OpenCTIConnector instance. + + :return: The OpenCTIConnector instance + :rtype: OpenCTIConnector + """ return self.connector def date_now(self) -> str: @@ -2293,6 +2451,17 @@ def get_attribute_in_mitre_extension(key, object) -> any: return None def get_data_from_enrichment(self, data, standard_id, opencti_entity): + """Extract STIX entity and objects from enrichment data. + + :param data: The enrichment data containing a bundle + :type data: dict + :param standard_id: The STIX standard ID of the entity + :type standard_id: str + :param opencti_entity: The OpenCTI entity object + :type opencti_entity: dict + :return: Dictionary containing stix_entity and stix_objects + :rtype: dict + """ bundle = data.get("bundle", None) # Extract main entity from bundle in case of playbook if bundle is None: diff --git a/pycti/entities/opencti_attack_pattern.py b/pycti/entities/opencti_attack_pattern.py index 531dbe89..01f31a1f 100644 --- a/pycti/entities/opencti_attack_pattern.py +++ b/pycti/entities/opencti_attack_pattern.py @@ -7,6 +7,11 @@ class AttackPattern: + """Main AttackPattern class for OpenCTI + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ @@ -238,6 +243,15 @@ def __init__(self, opencti): @staticmethod def generate_id(name, x_mitre_id=None): + """Generate a STIX ID for an Attack Pattern. + + :param name: The name of the attack pattern + :type name: str + :param x_mitre_id: Optional MITRE ATT&CK ID + :type x_mitre_id: str or None + :return: STIX ID for the attack pattern + :rtype: str + """ if x_mitre_id is not None: data = {"x_mitre_id": x_mitre_id.strip()} else: @@ -248,20 +262,32 @@ def generate_id(name, x_mitre_id=None): @staticmethod def generate_id_from_data(data): + """Generate a STIX ID from attack pattern data. + + :param data: Dictionary containing 'name' and optionally 'x_mitre_id' keys + :type data: dict + :return: STIX ID for the attack pattern + :rtype: str + """ external_id = data.get("x_mitre_id") or data.get("x_opencti_external_id") return AttackPattern.generate_id(data.get("name"), external_id) - """ - List Attack-Pattern objects + def list(self, **kwargs): + """List Attack Pattern objects. :param filters: the filters to apply :param search: the search keyword :param first: return the first n rows from the after ID (or the beginning if not set) :param after: ID of the first row for pagination - :return List of Attack-Pattern objects - """ - - def list(self, **kwargs): + :param orderBy: field to order results by + :param orderMode: ordering mode (asc/desc) + :param customAttributes: custom attributes to return + :param getAll: whether to retrieve all results + :param withPagination: whether to include pagination info + :param withFiles: whether to include files + :return: List of Attack Pattern objects + :rtype: list + """ filters = kwargs.get("filters", None) search = kwargs.get("search", None) first = kwargs.get("first", 500) diff --git a/pycti/entities/opencti_campaign.py b/pycti/entities/opencti_campaign.py index 9a7caec0..8a84d992 100644 --- a/pycti/entities/opencti_campaign.py +++ b/pycti/entities/opencti_campaign.py @@ -7,6 +7,11 @@ class Campaign: + """Main Campaign class for OpenCTI + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ @@ -226,6 +231,13 @@ def __init__(self, opencti): @staticmethod def generate_id(name): + """Generate a STIX ID for a Campaign. + + :param name: The name of the campaign + :type name: str + :return: STIX ID for the campaign + :rtype: str + """ name = name.lower().strip() data = {"name": name} data = canonicalize(data, utf8=False) @@ -234,19 +246,31 @@ def generate_id(name): @staticmethod def generate_id_from_data(data): + """Generate a STIX ID from campaign data. + + :param data: Dictionary containing 'name' key + :type data: dict + :return: STIX ID for the campaign + :rtype: str + """ return Campaign.generate_id(data["name"]) - """ - List Campaign objects + def list(self, **kwargs): + """List Campaign objects. :param filters: the filters to apply :param search: the search keyword :param first: return the first n rows from the after ID (or the beginning if not set) :param after: ID of the first row for pagination - :return List of Campaign objects - """ - - def list(self, **kwargs): + :param orderBy: field to order results by + :param orderMode: ordering mode (asc/desc) + :param customAttributes: custom attributes to return + :param getAll: whether to retrieve all results + :param withPagination: whether to include pagination info + :param withFiles: whether to include files + :return: List of Campaign objects + :rtype: list + """ filters = kwargs.get("filters", None) search = kwargs.get("search", None) first = kwargs.get("first", 500) diff --git a/pycti/entities/opencti_case_incident.py b/pycti/entities/opencti_case_incident.py index 332ce78b..ce361da3 100644 --- a/pycti/entities/opencti_case_incident.py +++ b/pycti/entities/opencti_case_incident.py @@ -7,6 +7,13 @@ class CaseIncident: + """Main CaseIncident class for OpenCTI + + Manages incident response cases in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/entities/opencti_case_rfi.py b/pycti/entities/opencti_case_rfi.py index 9b7e6cff..9184b2f1 100644 --- a/pycti/entities/opencti_case_rfi.py +++ b/pycti/entities/opencti_case_rfi.py @@ -7,6 +7,13 @@ class CaseRfi: + """Main CaseRfi (Request for Information) class for OpenCTI + + Manages RFI cases in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/entities/opencti_case_rft.py b/pycti/entities/opencti_case_rft.py index 5c57a02d..92dded3f 100644 --- a/pycti/entities/opencti_case_rft.py +++ b/pycti/entities/opencti_case_rft.py @@ -7,6 +7,13 @@ class CaseRft: + """Main CaseRft (Request for Takedown) class for OpenCTI + + Manages RFT cases in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/entities/opencti_channel.py b/pycti/entities/opencti_channel.py index d8a11085..e7783cb7 100644 --- a/pycti/entities/opencti_channel.py +++ b/pycti/entities/opencti_channel.py @@ -7,6 +7,13 @@ class Channel: + """Main Channel class for OpenCTI + + Manages communication channels used by threat actors in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/entities/opencti_course_of_action.py b/pycti/entities/opencti_course_of_action.py index 32d715ff..c2b0b5ee 100644 --- a/pycti/entities/opencti_course_of_action.py +++ b/pycti/entities/opencti_course_of_action.py @@ -7,6 +7,13 @@ class CourseOfAction: + """Main CourseOfAction class for OpenCTI + + Manages courses of action (mitigations) in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/entities/opencti_data_component.py b/pycti/entities/opencti_data_component.py index 8c104be8..f0bb7e09 100644 --- a/pycti/entities/opencti_data_component.py +++ b/pycti/entities/opencti_data_component.py @@ -7,6 +7,13 @@ class DataComponent: + """Main DataComponent class for OpenCTI + + Manages MITRE ATT&CK data components in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/entities/opencti_data_source.py b/pycti/entities/opencti_data_source.py index fa1b787b..5410fe52 100644 --- a/pycti/entities/opencti_data_source.py +++ b/pycti/entities/opencti_data_source.py @@ -7,6 +7,13 @@ class DataSource: + """Main DataSource class for OpenCTI + + Manages MITRE ATT&CK data sources in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/entities/opencti_event.py b/pycti/entities/opencti_event.py index f9d97e65..7e961a8b 100644 --- a/pycti/entities/opencti_event.py +++ b/pycti/entities/opencti_event.py @@ -7,6 +7,13 @@ class Event: + """Main Event class for OpenCTI + + Manages security events in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ @@ -226,6 +233,13 @@ def __init__(self, opencti): @staticmethod def generate_id(name): + """Generate a STIX ID for an Event. + + :param name: The name of the event + :type name: str + :return: STIX ID for the event + :rtype: str + """ name = name.lower().strip() data = {"name": name} data = canonicalize(data, utf8=False) @@ -234,19 +248,31 @@ def generate_id(name): @staticmethod def generate_id_from_data(data): + """Generate a STIX ID from event data. + + :param data: Dictionary containing 'name' key + :type data: dict + :return: STIX ID for the event + :rtype: str + """ return Event.generate_id(data["name"]) - """ - List Event objects + def list(self, **kwargs): + """List Event objects. :param filters: the filters to apply :param search: the search keyword :param first: return the first n rows from the after ID (or the beginning if not set) :param after: ID of the first row for pagination - :return List of Event objects - """ - - def list(self, **kwargs): + :param orderBy: field to order results by + :param orderMode: ordering mode (asc/desc) + :param customAttributes: custom attributes to return + :param getAll: whether to retrieve all results + :param withPagination: whether to include pagination info + :param withFiles: whether to include files + :return: List of Event objects + :rtype: list + """ filters = kwargs.get("filters", None) search = kwargs.get("search", None) first = kwargs.get("first", 100) diff --git a/pycti/entities/opencti_external_reference.py b/pycti/entities/opencti_external_reference.py index 59fd6dd2..1eee6a18 100644 --- a/pycti/entities/opencti_external_reference.py +++ b/pycti/entities/opencti_external_reference.py @@ -9,6 +9,14 @@ class ExternalReference: + """Main ExternalReference class for OpenCTI + + Manages external references and citations in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + :param file: file handling configuration + """ + def __init__(self, opencti, file): self.opencti = opencti self.file = file diff --git a/pycti/entities/opencti_feedback.py b/pycti/entities/opencti_feedback.py index d34e4466..67323408 100644 --- a/pycti/entities/opencti_feedback.py +++ b/pycti/entities/opencti_feedback.py @@ -6,6 +6,13 @@ class Feedback: + """Main Feedback class for OpenCTI + + Manages feedback and analyst assessments in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/entities/opencti_grouping.py b/pycti/entities/opencti_grouping.py index b998707a..f7c18865 100644 --- a/pycti/entities/opencti_grouping.py +++ b/pycti/entities/opencti_grouping.py @@ -8,6 +8,13 @@ class Grouping: + """Main Grouping class for OpenCTI + + Manages STIX grouping objects in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/entities/opencti_identity.py b/pycti/entities/opencti_identity.py index 09abd195..20272878 100644 --- a/pycti/entities/opencti_identity.py +++ b/pycti/entities/opencti_identity.py @@ -9,6 +9,13 @@ class Identity: + """Main Identity class for OpenCTI + + Manages individual, organization, and system identities in OpenCTI. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ @@ -258,6 +265,15 @@ def __init__(self, opencti): @staticmethod def generate_id(name, identity_class): + """Generate a STIX ID for an Identity. + + :param name: The name of the identity + :type name: str + :param identity_class: The class of the identity (individual, group, organization, etc.) + :type identity_class: str + :return: STIX ID for the identity + :rtype: str + """ data = {"name": name.lower().strip(), "identity_class": identity_class.lower()} data = canonicalize(data, utf8=False) id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data)) @@ -265,20 +281,32 @@ def generate_id(name, identity_class): @staticmethod def generate_id_from_data(data): + """Generate a STIX ID from identity data. + + :param data: Dictionary containing 'name' and 'identity_class' keys + :type data: dict + :return: STIX ID for the identity + :rtype: str + """ return Identity.generate_id(data["name"], data["identity_class"]) - """ - List Identity objects + def list(self, **kwargs): + """List Identity objects. :param types: the list of types :param filters: the filters to apply :param search: the search keyword :param first: return the first n rows from the after ID (or the beginning if not set) :param after: ID of the first row for pagination - :return List of Identity objects - """ - - def list(self, **kwargs): + :param orderBy: field to order results by + :param orderMode: ordering mode (asc/desc) + :param customAttributes: custom attributes to return + :param getAll: whether to retrieve all results + :param withPagination: whether to include pagination info + :param withFiles: whether to include files + :return: List of Identity objects + :rtype: list + """ types = kwargs.get("types", None) filters = kwargs.get("filters", None) search = kwargs.get("search", None) diff --git a/pycti/entities/opencti_incident.py b/pycti/entities/opencti_incident.py index e35a4cc4..ea7bb115 100644 --- a/pycti/entities/opencti_incident.py +++ b/pycti/entities/opencti_incident.py @@ -8,6 +8,13 @@ class Incident: + """Main Incident class for OpenCTI + + Manages security incidents in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/entities/opencti_indicator.py b/pycti/entities/opencti_indicator.py index 0f3b2639..972099f0 100644 --- a/pycti/entities/opencti_indicator.py +++ b/pycti/entities/opencti_indicator.py @@ -24,6 +24,13 @@ def __init__(self, opencti): @staticmethod def generate_id(pattern): + """Generate a STIX ID for an Indicator. + + :param pattern: The STIX pattern + :type pattern: str + :return: STIX ID for the indicator + :rtype: str + """ data = {"pattern": pattern.strip()} data = canonicalize(data, utf8=False) id = str(uuid.uuid5(uuid.UUID("00abedb4-aa42-466c-9c01-fed23315a9b7"), data)) @@ -31,6 +38,13 @@ def generate_id(pattern): @staticmethod def generate_id_from_data(data): + """Generate a STIX ID from indicator data. + + :param data: Dictionary containing 'pattern' key + :type data: dict + :return: STIX ID for the indicator + :rtype: str + """ return Indicator.generate_id(data["pattern"]) def list(self, **kwargs): @@ -301,14 +315,14 @@ def create(self, **kwargs): "name or pattern or pattern_type or x_opencti_main_observable_type" ) - """ - Update an Indicator object field + def update_field(self, **kwargs): + """Update an Indicator object field. :param id: the Indicator id :param input: the input of the field - """ - - def update_field(self, **kwargs): + :return: Updated indicator object + :rtype: dict or None + """ id = kwargs.get("id", None) input = kwargs.get("input", None) if id is not None and input is not None: diff --git a/pycti/entities/opencti_intrusion_set.py b/pycti/entities/opencti_intrusion_set.py index 9b9d04eb..6a4bbe8d 100644 --- a/pycti/entities/opencti_intrusion_set.py +++ b/pycti/entities/opencti_intrusion_set.py @@ -7,6 +7,13 @@ class IntrusionSet: + """Main IntrusionSet class for OpenCTI + + Manages intrusion sets (APT groups) in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ @@ -232,6 +239,13 @@ def __init__(self, opencti): @staticmethod def generate_id(name): + """Generate a STIX ID for an Intrusion Set. + + :param name: The name of the intrusion set + :type name: str + :return: STIX ID for the intrusion set + :rtype: str + """ name = name.lower().strip() data = {"name": name} data = canonicalize(data, utf8=False) @@ -240,19 +254,31 @@ def generate_id(name): @staticmethod def generate_id_from_data(data): + """Generate a STIX ID from intrusion set data. + + :param data: Dictionary containing 'name' key + :type data: dict + :return: STIX ID for the intrusion set + :rtype: str + """ return IntrusionSet.generate_id(data["name"]) - """ - List Intrusion-Set objects + def list(self, **kwargs): + """List Intrusion Set objects. :param filters: the filters to apply :param search: the search keyword :param first: return the first n rows from the after ID (or the beginning if not set) :param after: ID of the first row for pagination - :return List of Intrusion-Set objects - """ - - def list(self, **kwargs): + :param orderBy: field to order results by + :param orderMode: ordering mode (asc/desc) + :param customAttributes: custom attributes to return + :param getAll: whether to retrieve all results + :param withPagination: whether to include pagination info + :param withFiles: whether to include files + :return: List of Intrusion Set objects + :rtype: list + """ filters = kwargs.get("filters", None) search = kwargs.get("search", None) first = kwargs.get("first", 500) @@ -328,15 +354,16 @@ def list(self, **kwargs): result["data"]["intrusionSets"], with_pagination ) - """ - Read a Intrusion-Set object + def read(self, **kwargs): + """Read an Intrusion Set object. - :param id: the id of the Intrusion-Set + :param id: the id of the Intrusion Set :param filters: the filters to apply if no id provided - :return Intrusion-Set object - """ - - def read(self, **kwargs): + :param customAttributes: custom attributes to return + :param withFiles: whether to include files + :return: Intrusion Set object + :rtype: dict or None + """ id = kwargs.get("id", None) filters = kwargs.get("filters", None) custom_attributes = kwargs.get("customAttributes", None) @@ -372,14 +399,26 @@ def read(self, **kwargs): ) return None - """ - Create a Intrusion-Set object + def create(self, **kwargs): + """Create an Intrusion Set object. :param name: the name of the Intrusion Set - :return Intrusion-Set object - """ - - def create(self, **kwargs): + :param description: description of the intrusion set + :param aliases: list of aliases + :param first_seen: first seen date + :param last_seen: last seen date + :param goals: goals of the intrusion set + :param resource_level: resource level + :param primary_motivation: primary motivation + :param secondary_motivations: secondary motivations + :param createdBy: creator identity + :param objectMarking: marking definitions + :param objectLabel: labels + :param externalReferences: external references + :param update: whether to update existing intrusion set + :return: Intrusion Set object + :rtype: dict or None + """ stix_id = kwargs.get("stix_id", None) created_by = kwargs.get("createdBy", None) object_marking = kwargs.get("objectMarking", None) diff --git a/pycti/entities/opencti_kill_chain_phase.py b/pycti/entities/opencti_kill_chain_phase.py index 56d7ca30..0a2fdb71 100644 --- a/pycti/entities/opencti_kill_chain_phase.py +++ b/pycti/entities/opencti_kill_chain_phase.py @@ -6,6 +6,13 @@ class KillChainPhase: + """Main KillChainPhase class for OpenCTI + + Manages kill chain phases (ATT&CK tactics) in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/entities/opencti_label.py b/pycti/entities/opencti_label.py index c17f2863..efbe2770 100644 --- a/pycti/entities/opencti_label.py +++ b/pycti/entities/opencti_label.py @@ -7,6 +7,13 @@ class Label: + """Main Label class for OpenCTI + + Manages labels and tags in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/entities/opencti_language.py b/pycti/entities/opencti_language.py index ec064d1a..8d02b8ac 100644 --- a/pycti/entities/opencti_language.py +++ b/pycti/entities/opencti_language.py @@ -7,6 +7,13 @@ class Language: + """Main Language class for OpenCTI + + Manages language entities in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/entities/opencti_location.py b/pycti/entities/opencti_location.py index f0b3da9f..c744249e 100644 --- a/pycti/entities/opencti_location.py +++ b/pycti/entities/opencti_location.py @@ -7,6 +7,13 @@ class Location: + """Main Location class for OpenCTI + + Manages geographic locations (countries, cities, regions) in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/entities/opencti_malware.py b/pycti/entities/opencti_malware.py index 22627ee0..9193710a 100644 --- a/pycti/entities/opencti_malware.py +++ b/pycti/entities/opencti_malware.py @@ -7,6 +7,13 @@ class Malware: + """Main Malware class for OpenCTI + + Manages malware families and variants in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ @@ -260,6 +267,13 @@ def __init__(self, opencti): @staticmethod def generate_id(name): + """Generate a STIX ID for a Malware. + + :param name: The name of the malware + :type name: str + :return: STIX ID for the malware + :rtype: str + """ name = name.lower().strip() data = {"name": name} data = canonicalize(data, utf8=False) @@ -268,19 +282,31 @@ def generate_id(name): @staticmethod def generate_id_from_data(data): + """Generate a STIX ID from malware data. + + :param data: Dictionary containing 'name' key + :type data: dict + :return: STIX ID for the malware + :rtype: str + """ return Malware.generate_id(data["name"]) - """ - List Malware objects + def list(self, **kwargs): + """List Malware objects. :param filters: the filters to apply :param search: the search keyword :param first: return the first n rows from the after ID (or the beginning if not set) :param after: ID of the first row for pagination - :return List of Malware objects - """ - - def list(self, **kwargs): + :param orderBy: field to order results by + :param orderMode: ordering mode (asc/desc) + :param customAttributes: custom attributes to return + :param getAll: whether to retrieve all results + :param withPagination: whether to include pagination info + :param withFiles: whether to include files + :return: List of Malware objects + :rtype: list + """ filters = kwargs.get("filters", None) search = kwargs.get("search", None) first = kwargs.get("first", 500) @@ -359,15 +385,16 @@ def list(self, **kwargs): result["data"]["malwares"], with_pagination ) - """ - Read a Malware object + def read(self, **kwargs): + """Read a Malware object. :param id: the id of the Malware :param filters: the filters to apply if no id provided - :return Malware object - """ - - def read(self, **kwargs): + :param customAttributes: custom attributes to return + :param withFiles: whether to include files + :return: Malware object + :rtype: dict or None + """ id = kwargs.get("id", None) filters = kwargs.get("filters", None) custom_attributes = kwargs.get("customAttributes", None) @@ -403,14 +430,29 @@ def read(self, **kwargs): ) return None - """ - Create a Malware object + def create(self, **kwargs): + """Create a Malware object. :param name: the name of the Malware - :return Malware object - """ - - def create(self, **kwargs): + :param description: description of the malware + :param aliases: list of aliases + :param malware_types: types of malware + :param is_family: whether this is a malware family + :param first_seen: first seen date + :param last_seen: last seen date + :param architecture_execution_envs: execution environments + :param implementation_languages: implementation languages + :param capabilities: malware capabilities + :param killChainPhases: kill chain phases + :param samples: malware samples + :param createdBy: creator identity + :param objectMarking: marking definitions + :param objectLabel: labels + :param externalReferences: external references + :param update: whether to update existing malware + :return: Malware object + :rtype: dict or None + """ stix_id = kwargs.get("stix_id", None) created_by = kwargs.get("createdBy", None) object_marking = kwargs.get("objectMarking", None) diff --git a/pycti/entities/opencti_malware_analysis.py b/pycti/entities/opencti_malware_analysis.py index 47251faa..7c941187 100644 --- a/pycti/entities/opencti_malware_analysis.py +++ b/pycti/entities/opencti_malware_analysis.py @@ -7,6 +7,13 @@ class MalwareAnalysis: + """Main MalwareAnalysis class for OpenCTI + + Manages malware analysis reports and results in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/entities/opencti_marking_definition.py b/pycti/entities/opencti_marking_definition.py index cf5bf152..01ac5267 100644 --- a/pycti/entities/opencti_marking_definition.py +++ b/pycti/entities/opencti_marking_definition.py @@ -7,6 +7,13 @@ class MarkingDefinition: + """Main MarkingDefinition class for OpenCTI + + Manages marking definitions (TLP, statements) in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/entities/opencti_narrative.py b/pycti/entities/opencti_narrative.py index 28860b2a..64351a06 100644 --- a/pycti/entities/opencti_narrative.py +++ b/pycti/entities/opencti_narrative.py @@ -7,6 +7,13 @@ class Narrative: + """Main Narrative class for OpenCTI + + Manages narratives and disinformation campaigns in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/entities/opencti_note.py b/pycti/entities/opencti_note.py index 0cdba322..f6db4090 100644 --- a/pycti/entities/opencti_note.py +++ b/pycti/entities/opencti_note.py @@ -8,6 +8,13 @@ class Note: + """Main Note class for OpenCTI + + Manages notes and annotations in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/entities/opencti_observed_data.py b/pycti/entities/opencti_observed_data.py index 163cf328..5980227f 100644 --- a/pycti/entities/opencti_observed_data.py +++ b/pycti/entities/opencti_observed_data.py @@ -7,6 +7,13 @@ class ObservedData: + """Main ObservedData class for OpenCTI + + Manages observed data and raw intelligence in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/entities/opencti_opinion.py b/pycti/entities/opencti_opinion.py index e90b869c..66d1327f 100644 --- a/pycti/entities/opencti_opinion.py +++ b/pycti/entities/opencti_opinion.py @@ -7,6 +7,13 @@ class Opinion: + """Main Opinion class for OpenCTI + + Manages analyst opinions and assessments in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/entities/opencti_report.py b/pycti/entities/opencti_report.py index 58d8e1e1..3c5076a9 100644 --- a/pycti/entities/opencti_report.py +++ b/pycti/entities/opencti_report.py @@ -9,6 +9,13 @@ class Report: + """Main Report class for OpenCTI + + Manages threat intelligence reports in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/entities/opencti_stix.py b/pycti/entities/opencti_stix.py index ffd3e1a1..10ca0f7a 100644 --- a/pycti/entities/opencti_stix.py +++ b/pycti/entities/opencti_stix.py @@ -1,4 +1,11 @@ class Stix: + """Main Stix class for OpenCTI + + Provides generic STIX object operations in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti diff --git a/pycti/entities/opencti_stix_core_object.py b/pycti/entities/opencti_stix_core_object.py index 0415a726..f0a92e22 100644 --- a/pycti/entities/opencti_stix_core_object.py +++ b/pycti/entities/opencti_stix_core_object.py @@ -3,6 +3,14 @@ class StixCoreObject: + """Main StixCoreObject class for OpenCTI + + Base class for managing STIX core objects in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + :param file: file handling configuration + """ + def __init__(self, opencti, file): self.opencti = opencti self.file = file diff --git a/pycti/entities/opencti_stix_core_relationship.py b/pycti/entities/opencti_stix_core_relationship.py index 8a40db70..dcd92673 100644 --- a/pycti/entities/opencti_stix_core_relationship.py +++ b/pycti/entities/opencti_stix_core_relationship.py @@ -7,6 +7,13 @@ class StixCoreRelationship: + """Main StixCoreRelationship class for OpenCTI + + Manages STIX relationships between entities in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/entities/opencti_stix_cyber_observable.py b/pycti/entities/opencti_stix_cyber_observable.py index f36c026a..5541ab1b 100644 --- a/pycti/entities/opencti_stix_cyber_observable.py +++ b/pycti/entities/opencti_stix_cyber_observable.py @@ -17,6 +17,15 @@ class StixCyberObservable(StixCyberObservableDeprecatedMixin): + """Main StixCyberObservable class for OpenCTI + + Manages STIX cyber observables (indicators of compromise) in the OpenCTI platform. + Note: Deprecated methods are available through StixCyberObservableDeprecatedMixin. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + :param file: file handling configuration + """ + def __init__(self, opencti, file): self.opencti = opencti diff --git a/pycti/entities/opencti_stix_domain_object.py b/pycti/entities/opencti_stix_domain_object.py index f2b5190d..64b0eda4 100644 --- a/pycti/entities/opencti_stix_domain_object.py +++ b/pycti/entities/opencti_stix_domain_object.py @@ -7,6 +7,14 @@ class StixDomainObject: + """Main StixDomainObject class for OpenCTI + + Manages STIX Domain Objects in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + :param file: file handling configuration + """ + def __init__(self, opencti, file): self.opencti = opencti self.file = file diff --git a/pycti/entities/opencti_stix_nested_ref_relationship.py b/pycti/entities/opencti_stix_nested_ref_relationship.py index b59959c7..36902289 100644 --- a/pycti/entities/opencti_stix_nested_ref_relationship.py +++ b/pycti/entities/opencti_stix_nested_ref_relationship.py @@ -1,4 +1,11 @@ class StixNestedRefRelationship: + """Main StixNestedRefRelationship class for OpenCTI + + Manages nested reference relationships in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/entities/opencti_stix_object_or_stix_relationship.py b/pycti/entities/opencti_stix_object_or_stix_relationship.py index 18a4629e..036e9ea2 100644 --- a/pycti/entities/opencti_stix_object_or_stix_relationship.py +++ b/pycti/entities/opencti_stix_object_or_stix_relationship.py @@ -2,6 +2,13 @@ class StixObjectOrStixRelationship: + """Main StixObjectOrStixRelationship class for OpenCTI + + Manages generic STIX objects and relationships in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/entities/opencti_stix_sighting_relationship.py b/pycti/entities/opencti_stix_sighting_relationship.py index 977c341a..30b1dc6b 100644 --- a/pycti/entities/opencti_stix_sighting_relationship.py +++ b/pycti/entities/opencti_stix_sighting_relationship.py @@ -7,6 +7,13 @@ class StixSightingRelationship: + """Main StixSightingRelationship class for OpenCTI + + Manages STIX sighting relationships in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/entities/opencti_task.py b/pycti/entities/opencti_task.py index ec4ec768..91d44f04 100644 --- a/pycti/entities/opencti_task.py +++ b/pycti/entities/opencti_task.py @@ -7,6 +7,13 @@ class Task: + """Main Task class for OpenCTI + + Manages tasks and to-do items in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/entities/opencti_tool.py b/pycti/entities/opencti_tool.py index ccbab034..f90076d8 100644 --- a/pycti/entities/opencti_tool.py +++ b/pycti/entities/opencti_tool.py @@ -7,6 +7,13 @@ class Tool: + """Main Tool class for OpenCTI + + Manages tools used by threat actors in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ @@ -136,6 +143,13 @@ def __init__(self, opencti): @staticmethod def generate_id(name): + """Generate a STIX ID for a Tool. + + :param name: The name of the tool + :type name: str + :return: STIX ID for the tool + :rtype: str + """ name = name.lower().strip() data = {"name": name} data = canonicalize(data, utf8=False) @@ -144,19 +158,30 @@ def generate_id(name): @staticmethod def generate_id_from_data(data): + """Generate a STIX ID from tool data. + + :param data: Dictionary containing 'name' key + :type data: dict + :return: STIX ID for the tool + :rtype: str + """ return Tool.generate_id(data["name"]) - """ - List Tool objects + def list(self, **kwargs): + """List Tool objects. :param filters: the filters to apply :param search: the search keyword :param first: return the first n rows from the after ID (or the beginning if not set) :param after: ID of the first row for pagination - :return List of Tool objects - """ - - def list(self, **kwargs): + :param orderBy: field to order results by + :param orderMode: ordering mode (asc/desc) + :param customAttributes: custom attributes to return + :param getAll: whether to retrieve all results + :param withPagination: whether to include pagination info + :return: List of Tool objects + :rtype: list + """ filters = kwargs.get("filters", None) search = kwargs.get("search", None) first = kwargs.get("first", 100) @@ -229,15 +254,15 @@ def list(self, **kwargs): result["data"]["tools"], with_pagination ) - """ - Read a Tool object + def read(self, **kwargs): + """Read a Tool object. :param id: the id of the Tool :param filters: the filters to apply if no id provided - :return Tool object - """ - - def read(self, **kwargs): + :param customAttributes: custom attributes to return + :return: Tool object + :rtype: dict or None + """ id = kwargs.get("id", None) filters = kwargs.get("filters", None) custom_attributes = kwargs.get("customAttributes", None) @@ -272,14 +297,23 @@ def read(self, **kwargs): ) return None - """ - Create a Tool object + def create(self, **kwargs): + """Create a Tool object. :param name: the name of the Tool - :return Tool object - """ - - def create(self, **kwargs): + :param description: description of the tool + :param aliases: list of aliases + :param tool_types: types of tool + :param tool_version: version of the tool + :param killChainPhases: kill chain phases + :param createdBy: creator identity + :param objectMarking: marking definitions + :param objectLabel: labels + :param externalReferences: external references + :param update: whether to update existing tool + :return: Tool object + :rtype: dict or None + """ stix_id = kwargs.get("stix_id", None) created_by = kwargs.get("createdBy", None) object_marking = kwargs.get("objectMarking", None) diff --git a/pycti/entities/opencti_vocabulary.py b/pycti/entities/opencti_vocabulary.py index c18710ba..c0951a08 100644 --- a/pycti/entities/opencti_vocabulary.py +++ b/pycti/entities/opencti_vocabulary.py @@ -5,6 +5,13 @@ class Vocabulary: + """Main Vocabulary class for OpenCTI + + Manages vocabularies and controlled vocabularies in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/entities/opencti_vulnerability.py b/pycti/entities/opencti_vulnerability.py index 2d6a55d0..545827a9 100644 --- a/pycti/entities/opencti_vulnerability.py +++ b/pycti/entities/opencti_vulnerability.py @@ -7,6 +7,13 @@ class Vulnerability: + """Main Vulnerability class for OpenCTI + + Manages vulnerability information including CVE data in the OpenCTI platform. + + :param opencti: instance of :py:class:`~pycti.api.opencti_api_client.OpenCTIApiClient` + """ + def __init__(self, opencti): self.opencti = opencti self.properties = """ diff --git a/pycti/utils/opencti_logger.py b/pycti/utils/opencti_logger.py index 48ad51fe..80428b11 100644 --- a/pycti/utils/opencti_logger.py +++ b/pycti/utils/opencti_logger.py @@ -5,7 +5,15 @@ class CustomJsonFormatter(jsonlogger.JsonFormatter): + """Custom JSON formatter for structured logging.""" + def add_fields(self, log_record, record, message_dict): + """Add custom fields to the log record. + + :param log_record: The log record dictionary + :param record: The LogRecord instance + :param message_dict: The message dictionary + """ super(CustomJsonFormatter, self).add_fields(log_record, record, message_dict) if not log_record.get("timestamp"): # This doesn't use record.created, so it is slightly off @@ -18,6 +26,14 @@ def add_fields(self, log_record, record, message_dict): def logger(level, json_logging=True): + """Create a logger with JSON or standard formatting. + + :param level: Logging level (e.g., logging.INFO, logging.DEBUG) + :param json_logging: Whether to use JSON formatting for logs + :type json_logging: bool + :return: AppLogger class + :rtype: class + """ # Exceptions logging.getLogger("urllib3").setLevel(logging.WARNING) logging.getLogger("pika").setLevel(logging.ERROR) @@ -37,22 +53,63 @@ def __init__(self, name): @staticmethod def prepare_meta(meta=None): + """Prepare metadata for logging. + + :param meta: Metadata dictionary or None + :type meta: dict or None + :return: Formatted metadata or None + :rtype: dict or None + """ return None if meta is None else {"attributes": meta} @staticmethod def setup_logger_level(lib, log_level): + """Set the logging level for a specific library. + + :param lib: Library name + :type lib: str + :param log_level: Logging level to set + """ logging.getLogger(lib).setLevel(log_level) def debug(self, message, meta=None): + """Log a debug message. + + :param message: Message to log + :type message: str + :param meta: Optional metadata to include + :type meta: dict or None + """ self.local_logger.debug(message, extra=AppLogger.prepare_meta(meta)) def info(self, message, meta=None): + """Log an info message. + + :param message: Message to log + :type message: str + :param meta: Optional metadata to include + :type meta: dict or None + """ self.local_logger.info(message, extra=AppLogger.prepare_meta(meta)) def warning(self, message, meta=None): + """Log a warning message. + + :param message: Message to log + :type message: str + :param meta: Optional metadata to include + :type meta: dict or None + """ self.local_logger.warning(message, extra=AppLogger.prepare_meta(meta)) def error(self, message, meta=None): + """Log an error message with exception info. + + :param message: Message to log + :type message: str + :param meta: Optional metadata to include + :type meta: dict or None + """ # noinspection PyTypeChecker self.local_logger.error( message, exc_info=1, extra=AppLogger.prepare_meta(meta) diff --git a/pycti/utils/opencti_stix2.py b/pycti/utils/opencti_stix2.py index 4bd30564..69747e67 100644 --- a/pycti/utils/opencti_stix2.py +++ b/pycti/utils/opencti_stix2.py @@ -98,6 +98,11 @@ def __init__(self, opencti): ######### UTILS # region utils def unknown_type(self, stix_object: Dict) -> None: + """Log an error for unknown STIX object types. + + :param stix_object: STIX object with unknown type + :type stix_object: Dict + """ self.opencti.app_logger.error( "Unknown object type, doing nothing...", {"type": stix_object["type"]} ) @@ -116,6 +121,13 @@ def convert_markdown(self, text: str) -> str: return None def format_date(self, date: Any = None) -> str: + """Format a date to ISO 8601 string format. + + :param date: Date to format (various formats supported) + :type date: Any + :return: ISO 8601 formatted date string + :rtype: str + """ """converts multiple input date formats to OpenCTI style dates :param date: input date @@ -814,6 +826,11 @@ def extract_embedded_relationships( # Please use get_reader instead of this definition def get_readers(self): + """Get a dictionary mapping entity types to their read methods. + + :return: Dictionary mapping entity types to read functions + :rtype: dict + """ return { "Attack-Pattern": self.opencti.attack_pattern.read, "Campaign": self.opencti.campaign.read, @@ -861,6 +878,13 @@ def get_readers(self): } def get_reader(self, entity_type: str): + """Get the appropriate reader function for a given entity type. + + :param entity_type: Type of the entity + :type entity_type: str + :return: Reader function for the entity type + :rtype: callable or None + """ # Map types if entity_type == "StixFile": entity_type = "File" @@ -881,6 +905,11 @@ def get_reader(self, entity_type: str): # endregion def get_stix_helper(self): + """Get a dictionary mapping STIX types to their helper functions. + + :return: Dictionary mapping STIX types to generate_id functions + :rtype: dict + """ # Import return { # entities @@ -929,6 +958,11 @@ def get_stix_helper(self): } def get_internal_helper(self): + """Get a dictionary mapping internal types to their helper functions. + + :return: Dictionary mapping internal types to generate_id functions + :rtype: dict + """ # Import return { "user": self.opencti.user, @@ -947,6 +981,13 @@ def get_internal_helper(self): } def generate_standard_id_from_stix(self, data): + """Generate a standard ID from STIX data. + + :param data: STIX data dictionary + :type data: dict + :return: Generated standard ID or None + :rtype: str or None + """ stix_helpers = self.get_stix_helper() helper = stix_helpers.get(data["type"]) return helper.generate_id_from_data(data) diff --git a/pycti/utils/opencti_stix2_splitter.py b/pycti/utils/opencti_stix2_splitter.py index 23bf071c..b65ef25c 100644 --- a/pycti/utils/opencti_stix2_splitter.py +++ b/pycti/utils/opencti_stix2_splitter.py @@ -34,6 +34,11 @@ def is_id_supported(key): class OpenCTIStix2Splitter: + """STIX2 bundle splitter for OpenCTI + + Splits large STIX2 bundles into smaller chunks for processing. + """ + def __init__(self): self.cache_index = {} self.cache_refs = {} diff --git a/pycti/utils/opencti_stix2_utils.py b/pycti/utils/opencti_stix2_utils.py index ba472bdd..0d9a3eec 100644 --- a/pycti/utils/opencti_stix2_utils.py +++ b/pycti/utils/opencti_stix2_utils.py @@ -150,8 +150,20 @@ class OpenCTIStix2Utils: + """Utility class for STIX2 operations in OpenCTI + + Provides helper methods for STIX2 conversions and pattern generation. + """ + @staticmethod def stix_observable_opencti_type(observable_type): + """Convert STIX observable type to OpenCTI type. + + :param observable_type: STIX observable type + :type observable_type: str + :return: Corresponding OpenCTI type or "Unknown" + :rtype: str + """ if observable_type in STIX_CYBER_OBSERVABLE_MAPPING: return STIX_CYBER_OBSERVABLE_MAPPING[observable_type] else: @@ -159,6 +171,15 @@ def stix_observable_opencti_type(observable_type): @staticmethod def create_stix_pattern(observable_type, observable_value): + """Create a STIX pattern from an observable type and value. + + :param observable_type: Type of the observable + :type observable_type: str + :param observable_value: Value of the observable + :type observable_value: str + :return: STIX pattern string or None if type not supported + :rtype: str or None + """ if observable_type in PATTERN_MAPPING: lhs = ObjectPath( ( @@ -175,14 +196,16 @@ def create_stix_pattern(observable_type, observable_value): else: return None - """Generate random stix id (uuid v1) - This id will stored and resolved by openCTI - We will stored only 5 stix of this type to prevent database flooding - :param stix_type: the stix type - """ - @staticmethod def generate_random_stix_id(stix_type): + """Generate random stix id (uuid v1) - DEPRECATED. + + This function is deprecated and should not be used anymore. + Please use the generate_id function for SDO or proper SCO constructor. + + :param stix_type: the stix type + :raises ValueError: Always raises an error as this function is deprecated + """ raise ValueError( "This function should not be used anymore, please use the generate_id function for SDO or proper SCO constructor" ) @@ -191,6 +214,18 @@ def generate_random_stix_id(stix_type): def retrieveClassForMethod( openCTIApiClient, entity: Dict, type_path: str, method: str ) -> Any: + """Retrieve the appropriate API class for a given entity type and method. + + :param openCTIApiClient: OpenCTI API client instance + :param entity: Entity dictionary containing the type + :type entity: Dict + :param type_path: Path to the type field in the entity + :type type_path: str + :param method: Name of the method to check for + :type method: str + :return: The API class that has the specified method, or None + :rtype: Any + """ if entity is not None and type_path in entity: attributeName = entity[type_path].lower().replace("-", "_") if hasattr(openCTIApiClient, attributeName):