From 926225a32907e0ee7b8c292b58c9e79d7b627bf0 Mon Sep 17 00:00:00 2001 From: Jeremy Cloarec Date: Fri, 25 Apr 2025 12:41:07 +0200 Subject: [PATCH 1/6] [client] report work expectations without impersonated user --- pycti/utils/opencti_stix2.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pycti/utils/opencti_stix2.py b/pycti/utils/opencti_stix2.py index a303844c3..6c467471f 100644 --- a/pycti/utils/opencti_stix2.py +++ b/pycti/utils/opencti_stix2.py @@ -2500,6 +2500,9 @@ def import_item( if processing_count > MAX_PROCESSING_COUNT: if work_id is not None: item_str = json.dumps(item) + # report expectation without impersonated user + current_applicant_id = self.opencti.get_request_headers()["opencti-applicant-id"] + self.opencti.set_applicant_id_header('') self.opencti.work.report_expectation( work_id, { @@ -2509,6 +2512,7 @@ def import_item( ), }, ) + self.opencti.set_applicant_id_header(current_applicant_id) return False try: self.opencti.set_retry_number(processing_count) @@ -2645,7 +2649,11 @@ def import_item( ): self.import_object(item, update, types) if work_id is not None: + # report expectation without impersonated user + current_applicant_id = self.opencti.get_request_headers()["opencti-applicant-id"] + self.opencti.set_applicant_id_header('') self.opencti.work.report_expectation(work_id, None) + self.opencti.set_applicant_id_header(current_applicant_id) bundles_success_counter.add(1) return True except (RequestException, Timeout): @@ -2702,6 +2710,9 @@ def import_item( bundles_technical_error_counter.add(1) if work_id is not None: self.opencti.work.api.set_draft_id("") + # report expectation without impersonated user + current_applicant_id = self.opencti.get_request_headers()["opencti-applicant-id"] + self.opencti.set_applicant_id_header('') self.opencti.work.report_expectation( work_id, { @@ -2709,6 +2720,7 @@ def import_item( "source": "Draft in read only", }, ) + self.opencti.set_applicant_id_header(current_applicant_id) return False # Platform does not know what to do and raises an error: # That also works for missing reference with too much execution @@ -2716,6 +2728,9 @@ def import_item( bundles_technical_error_counter.add(1) if work_id is not None: item_str = json.dumps(item) + # report expectation without impersonated user + current_applicant_id = self.opencti.get_request_headers()["opencti-applicant-id"] + self.opencti.set_applicant_id_header('') self.opencti.work.report_expectation( work_id, { @@ -2727,6 +2742,7 @@ def import_item( ), }, ) + self.opencti.set_applicant_id_header(current_applicant_id) return False def import_bundle( From 8a798e4fd363b7abee4c2c7d20344936b6d99d36 Mon Sep 17 00:00:00 2001 From: Jeremy Cloarec Date: Fri, 25 Apr 2025 12:41:26 +0200 Subject: [PATCH 2/6] [client] report work expectations without impersonated user --- pycti/utils/opencti_stix2.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/pycti/utils/opencti_stix2.py b/pycti/utils/opencti_stix2.py index 6c467471f..e8aed0f6e 100644 --- a/pycti/utils/opencti_stix2.py +++ b/pycti/utils/opencti_stix2.py @@ -2501,8 +2501,10 @@ def import_item( if work_id is not None: item_str = json.dumps(item) # report expectation without impersonated user - current_applicant_id = self.opencti.get_request_headers()["opencti-applicant-id"] - self.opencti.set_applicant_id_header('') + current_applicant_id = self.opencti.get_request_headers()[ + "opencti-applicant-id" + ] + self.opencti.set_applicant_id_header("") self.opencti.work.report_expectation( work_id, { @@ -2650,8 +2652,10 @@ def import_item( self.import_object(item, update, types) if work_id is not None: # report expectation without impersonated user - current_applicant_id = self.opencti.get_request_headers()["opencti-applicant-id"] - self.opencti.set_applicant_id_header('') + current_applicant_id = self.opencti.get_request_headers()[ + "opencti-applicant-id" + ] + self.opencti.set_applicant_id_header("") self.opencti.work.report_expectation(work_id, None) self.opencti.set_applicant_id_header(current_applicant_id) bundles_success_counter.add(1) @@ -2711,8 +2715,10 @@ def import_item( if work_id is not None: self.opencti.work.api.set_draft_id("") # report expectation without impersonated user - current_applicant_id = self.opencti.get_request_headers()["opencti-applicant-id"] - self.opencti.set_applicant_id_header('') + current_applicant_id = self.opencti.get_request_headers()[ + "opencti-applicant-id" + ] + self.opencti.set_applicant_id_header("") self.opencti.work.report_expectation( work_id, { @@ -2729,8 +2735,10 @@ def import_item( if work_id is not None: item_str = json.dumps(item) # report expectation without impersonated user - current_applicant_id = self.opencti.get_request_headers()["opencti-applicant-id"] - self.opencti.set_applicant_id_header('') + current_applicant_id = self.opencti.get_request_headers()[ + "opencti-applicant-id" + ] + self.opencti.set_applicant_id_header("") self.opencti.work.report_expectation( work_id, { From 8f8eb58227cb35cab4f256b483e0e422f447192c Mon Sep 17 00:00:00 2001 From: Jeremy Cloarec Date: Wed, 21 May 2025 14:17:40 +0200 Subject: [PATCH 3/6] [client] always do work queries without impersonation --- pycti/api/opencti_api_client.py | 11 ++++++++--- pycti/api/opencti_api_work.py | 29 +++++++++++++++-------------- pycti/utils/opencti_stix2.py | 20 -------------------- 3 files changed, 23 insertions(+), 37 deletions(-) diff --git a/pycti/api/opencti_api_client.py b/pycti/api/opencti_api_client.py index bbde7d68b..c36daa3e6 100644 --- a/pycti/api/opencti_api_client.py +++ b/pycti/api/opencti_api_client.py @@ -264,13 +264,15 @@ def set_retry_number(self, retry_number): "" if retry_number is None else str(retry_number) ) - def query(self, query, variables=None): + def query(self, query, variables=None, disable_impersonate=False): """submit a query to the OpenCTI GraphQL API :param query: GraphQL query string :type query: str :param variables: GraphQL query variables, defaults to {} :type variables: dict, optional + :param disable_impersonate: removes impersonate header if set to True, defaults to False + :type disable_impersonate: bool, optional :return: returns the response json content :rtype: Any """ @@ -295,6 +297,9 @@ def query(self, query, variables=None): else: query_var[key] = val + query_headers = self.request_headers + if disable_impersonate and "opencti-applicant-id" in query_headers: + del query_headers["opencti"] # If yes, transform variable (file to null) and create multipart query if len(files_vars) > 0: multipart_data = { @@ -361,7 +366,7 @@ def query(self, query, variables=None): self.api_url, data=multipart_data, files=multipart_files, - headers=self.request_headers, + headers=query_headers, verify=self.ssl_verify, cert=self.cert, proxies=self.proxies, @@ -372,7 +377,7 @@ def query(self, query, variables=None): r = self.session.post( self.api_url, json={"query": query, "variables": variables}, - headers=self.request_headers, + headers=query_headers, verify=self.ssl_verify, cert=self.cert, proxies=self.proxies, diff --git a/pycti/api/opencti_api_work.py b/pycti/api/opencti_api_work.py index 4ef5dde86..5d11a0fbd 100644 --- a/pycti/api/opencti_api_work.py +++ b/pycti/api/opencti_api_work.py @@ -20,7 +20,7 @@ def to_received(self, work_id: str, message: str): } } """ - self.api.query(query, {"id": work_id, "message": message}) + self.api.query(query, {"id": work_id, "message": message}, True) def to_processed(self, work_id: str, message: str, in_error: bool = False): if self.api.bundle_send_to_queue: @@ -35,7 +35,7 @@ def to_processed(self, work_id: str, message: str, in_error: bool = False): } """ self.api.query( - query, {"id": work_id, "message": message, "inError": in_error} + query, {"id": work_id, "message": message, "inError": in_error}, True ) def ping(self, work_id: str): @@ -60,7 +60,7 @@ def report_expectation(self, work_id: str, error): } """ try: - self.api.query(query, {"id": work_id, "error": error}) + self.api.query(query, {"id": work_id, "error": error}, True) except: self.api.app_logger.error("Cannot report expectation") @@ -78,7 +78,9 @@ def add_expectations(self, work_id: str, expectations: int): } """ try: - self.api.query(query, {"id": work_id, "expectations": expectations}) + self.api.query( + query, {"id": work_id, "expectations": expectations}, True + ) except: self.api.app_logger.error("Cannot report expectation") @@ -96,7 +98,9 @@ def add_draft_context(self, work_id: str, draft_context: str): } """ try: - self.api.query(query, {"id": work_id, "draftContext": draft_context}) + self.api.query( + query, {"id": work_id, "draftContext": draft_context}, True + ) except: self.api.app_logger.error("Cannot report draft context") @@ -111,7 +115,9 @@ def initiate_work(self, connector_id: str, friendly_name: str) -> str: } """ work = self.api.query( - query, {"connectorId": connector_id, "friendlyName": friendly_name} + query, + {"connectorId": connector_id, "friendlyName": friendly_name}, + True, ) return work["data"]["workAdd"]["id"] @@ -122,10 +128,7 @@ def delete_work(self, work_id: str): delete } }""" - work = self.api.query( - query, - {"workId": work_id}, - ) + work = self.api.query(query, {"workId": work_id}, True) return work["data"] def wait_for_work_to_finish(self, work_id: str): @@ -179,10 +182,7 @@ def get_work(self, work_id: str) -> Dict: } } """ - result = self.api.query( - query, - {"id": work_id}, - ) + result = self.api.query(query, {"id": work_id}, True) return result["data"]["work"] def get_connector_works(self, connector_id: str) -> List[Dict]: @@ -243,6 +243,7 @@ def get_connector_works(self, connector_id: str) -> List[Dict]: "filterGroups": [], }, }, + True, ) result = result["data"]["works"]["edges"] return_value = [] diff --git a/pycti/utils/opencti_stix2.py b/pycti/utils/opencti_stix2.py index e8aed0f6e..4432ad2e7 100644 --- a/pycti/utils/opencti_stix2.py +++ b/pycti/utils/opencti_stix2.py @@ -2501,10 +2501,6 @@ def import_item( if work_id is not None: item_str = json.dumps(item) # report expectation without impersonated user - current_applicant_id = self.opencti.get_request_headers()[ - "opencti-applicant-id" - ] - self.opencti.set_applicant_id_header("") self.opencti.work.report_expectation( work_id, { @@ -2514,7 +2510,6 @@ def import_item( ), }, ) - self.opencti.set_applicant_id_header(current_applicant_id) return False try: self.opencti.set_retry_number(processing_count) @@ -2652,12 +2647,7 @@ def import_item( self.import_object(item, update, types) if work_id is not None: # report expectation without impersonated user - current_applicant_id = self.opencti.get_request_headers()[ - "opencti-applicant-id" - ] - self.opencti.set_applicant_id_header("") self.opencti.work.report_expectation(work_id, None) - self.opencti.set_applicant_id_header(current_applicant_id) bundles_success_counter.add(1) return True except (RequestException, Timeout): @@ -2715,10 +2705,6 @@ def import_item( if work_id is not None: self.opencti.work.api.set_draft_id("") # report expectation without impersonated user - current_applicant_id = self.opencti.get_request_headers()[ - "opencti-applicant-id" - ] - self.opencti.set_applicant_id_header("") self.opencti.work.report_expectation( work_id, { @@ -2726,7 +2712,6 @@ def import_item( "source": "Draft in read only", }, ) - self.opencti.set_applicant_id_header(current_applicant_id) return False # Platform does not know what to do and raises an error: # That also works for missing reference with too much execution @@ -2735,10 +2720,6 @@ def import_item( if work_id is not None: item_str = json.dumps(item) # report expectation without impersonated user - current_applicant_id = self.opencti.get_request_headers()[ - "opencti-applicant-id" - ] - self.opencti.set_applicant_id_header("") self.opencti.work.report_expectation( work_id, { @@ -2750,7 +2731,6 @@ def import_item( ), }, ) - self.opencti.set_applicant_id_header(current_applicant_id) return False def import_bundle( From 4d578df16eb39a74f39a191e8420daa36d2077a2 Mon Sep 17 00:00:00 2001 From: Jeremy Cloarec Date: Wed, 21 May 2025 14:23:00 +0200 Subject: [PATCH 4/6] [client] always do work queries without impersonation --- pycti/utils/opencti_stix2.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pycti/utils/opencti_stix2.py b/pycti/utils/opencti_stix2.py index 4432ad2e7..a303844c3 100644 --- a/pycti/utils/opencti_stix2.py +++ b/pycti/utils/opencti_stix2.py @@ -2500,7 +2500,6 @@ def import_item( if processing_count > MAX_PROCESSING_COUNT: if work_id is not None: item_str = json.dumps(item) - # report expectation without impersonated user self.opencti.work.report_expectation( work_id, { @@ -2646,7 +2645,6 @@ def import_item( ): self.import_object(item, update, types) if work_id is not None: - # report expectation without impersonated user self.opencti.work.report_expectation(work_id, None) bundles_success_counter.add(1) return True @@ -2704,7 +2702,6 @@ def import_item( bundles_technical_error_counter.add(1) if work_id is not None: self.opencti.work.api.set_draft_id("") - # report expectation without impersonated user self.opencti.work.report_expectation( work_id, { @@ -2719,7 +2716,6 @@ def import_item( bundles_technical_error_counter.add(1) if work_id is not None: item_str = json.dumps(item) - # report expectation without impersonated user self.opencti.work.report_expectation( work_id, { From 302849c713915be49c8b7cb21c5148a6f5176445 Mon Sep 17 00:00:00 2001 From: Jeremy Cloarec Date: Fri, 23 May 2025 16:50:42 +0200 Subject: [PATCH 5/6] [client] remove header if disable_impersonate enabled --- pycti/api/opencti_api_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pycti/api/opencti_api_client.py b/pycti/api/opencti_api_client.py index c36daa3e6..5a9fd4227 100644 --- a/pycti/api/opencti_api_client.py +++ b/pycti/api/opencti_api_client.py @@ -299,7 +299,7 @@ def query(self, query, variables=None, disable_impersonate=False): query_headers = self.request_headers if disable_impersonate and "opencti-applicant-id" in query_headers: - del query_headers["opencti"] + del query_headers["opencti-applicant-id"] # If yes, transform variable (file to null) and create multipart query if len(files_vars) > 0: multipart_data = { From 207db870b4bf96b3742f8e23abde1ae03d556a5e Mon Sep 17 00:00:00 2001 From: Jeremy Cloarec Date: Fri, 23 May 2025 16:54:13 +0200 Subject: [PATCH 6/6] [client] remove header if disable_impersonate enabled --- pycti/api/opencti_api_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pycti/api/opencti_api_client.py b/pycti/api/opencti_api_client.py index 5a9fd4227..9b9afbbb5 100644 --- a/pycti/api/opencti_api_client.py +++ b/pycti/api/opencti_api_client.py @@ -297,7 +297,7 @@ def query(self, query, variables=None, disable_impersonate=False): else: query_var[key] = val - query_headers = self.request_headers + query_headers = self.request_headers.copy() if disable_impersonate and "opencti-applicant-id" in query_headers: del query_headers["opencti-applicant-id"] # If yes, transform variable (file to null) and create multipart query