From fcb65f55e8b4a367280af695ff85ec162fe7e478 Mon Sep 17 00:00:00 2001 From: Robert <144030336+rbowden-r7@users.noreply.github.com> Date: Tue, 4 Feb 2025 11:21:10 +0000 Subject: [PATCH] SOAR-18579-Better error handling for invalid dns (#3065) * SOAR-18579-Better error handling for invalid dns * SOAR-18579-Better error handling for invalid dns * adding unit tests * bumping sdk to version 6.2.4 --- plugins/salesforce/.CHECKSUM | 8 ++-- plugins/salesforce/Dockerfile | 4 +- plugins/salesforce/bin/komand_salesforce | 2 +- plugins/salesforce/help.md | 1 + .../tasks/monitor_users/schema.py | 1 + .../salesforce/komand_salesforce/util/api.py | 34 +++++++++----- plugins/salesforce/plugin.spec.yaml | 5 ++- plugins/salesforce/setup.py | 21 ++++----- .../connection_unsupported_grant.json.inp | 13 ++++++ .../unsupported_grant_type.json.resp | 4 ++ .../salesforce/unit_test/test_connection.py | 6 +++ .../unit_test/test_monitor_users.py | 45 +++++++++++++++++++ plugins/salesforce/unit_test/util.py | 7 +++ 13 files changed, 121 insertions(+), 30 deletions(-) create mode 100644 plugins/salesforce/unit_test/inputs/connection_unsupported_grant.json.inp create mode 100644 plugins/salesforce/unit_test/responses/unsupported_grant_type.json.resp diff --git a/plugins/salesforce/.CHECKSUM b/plugins/salesforce/.CHECKSUM index d032f98755..60d9a9e757 100644 --- a/plugins/salesforce/.CHECKSUM +++ b/plugins/salesforce/.CHECKSUM @@ -1,7 +1,7 @@ { - "spec": "63b7270d95683b98e315808e4df20354", - "manifest": "391ed2bce80fc53ee24774278259d26e", - "setup": "295d03a5efdf6658a6a10babe80a9a06", + "spec": "ca0461f1e8b0955a13e183d053e50eda", + "manifest": "e5d067d713f2381cc4875c19c200e489", + "setup": "bfad8c5af259dc2c0235de74e1b7d8dd", "schemas": [ { "identifier": "advanced_search/schema.py", @@ -41,7 +41,7 @@ }, { "identifier": "monitor_users/schema.py", - "hash": "87d5000d4862847cd58fec2364b9458a" + "hash": "a5369928b0fb356372ffad936aa59153" } ] } \ No newline at end of file diff --git a/plugins/salesforce/Dockerfile b/plugins/salesforce/Dockerfile index 0ac02cee1d..9023a8cb2f 100755 --- a/plugins/salesforce/Dockerfile +++ b/plugins/salesforce/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=linux/amd64 rapid7/insightconnect-python-3-plugin:6.2.0 +FROM --platform=linux/amd64 rapid7/insightconnect-python-3-plugin:6.2.4 LABEL organization=rapid7 LABEL sdk=python @@ -12,7 +12,7 @@ RUN if [ -f requirements.txt ]; then pip install -r requirements.txt; fi ADD . /python/src -RUN python setup.py build && python setup.py install +RUN pip install . # User to run plugin code. The two supported users are: root, nobody USER nobody diff --git a/plugins/salesforce/bin/komand_salesforce b/plugins/salesforce/bin/komand_salesforce index b9aa91cd8f..599662079b 100755 --- a/plugins/salesforce/bin/komand_salesforce +++ b/plugins/salesforce/bin/komand_salesforce @@ -6,7 +6,7 @@ from sys import argv Name = "Salesforce" Vendor = "rapid7" -Version = "2.1.12" +Version = "2.1.13" Description = "[Salesforce](https://www.salesforce.com) is a CRM solution that brings together all customer information in a single, integrated platform that enables building a customer-centered business from marketing right through to sales, customer service and business analysis. The Salesforce plugin allows you to search, update, and manage salesforce records. This plugin utilizes the [Salesforce API](https://developer.salesforce.com/docs/atlas.en-us.216.0.api_rest.meta/api_rest/intro_what_is_rest_api.htm)" diff --git a/plugins/salesforce/help.md b/plugins/salesforce/help.md index 2df94f7c4f..3ca4d3d899 100644 --- a/plugins/salesforce/help.md +++ b/plugins/salesforce/help.md @@ -533,6 +533,7 @@ Example output: # Version History +* 2.1.13 - Task Monitor Users: improve error response to UI | Bump SDK to 6.2.4 * 2.1.12 - Task Monitor Users: ensure datetime includes microseconds | Bump SDK to 6.2.0 * 2.1.11 - Task Monitor Users: Return 500 for retry your request error | Bump SDK to 6.1.4 * 2.1.10 - Set Monitor Users task output length | Fix to remove whitespace from connection inputs diff --git a/plugins/salesforce/komand_salesforce/tasks/monitor_users/schema.py b/plugins/salesforce/komand_salesforce/tasks/monitor_users/schema.py index c3d480a0ca..8b73089877 100755 --- a/plugins/salesforce/komand_salesforce/tasks/monitor_users/schema.py +++ b/plugins/salesforce/komand_salesforce/tasks/monitor_users/schema.py @@ -43,6 +43,7 @@ class MonitorUsersOutput(insightconnect_plugin_runtime.Output): "type": "array", "title": "Users", "description": "Information about users, their login history and which users have been updated", + "items": {}, "required": [ "users" ], diff --git a/plugins/salesforce/komand_salesforce/util/api.py b/plugins/salesforce/komand_salesforce/util/api.py index a138ba566c..ad345b4de9 100644 --- a/plugins/salesforce/komand_salesforce/util/api.py +++ b/plugins/salesforce/komand_salesforce/util/api.py @@ -18,6 +18,7 @@ SOBJECT_UPDATED_USERS, ) from komand_salesforce.util.exceptions import ApiException +from requests.exceptions import ConnectionError as DNSError def rate_limiting(max_tries: int): @@ -190,17 +191,27 @@ def _get_token( client_url = f"https://{salesforce_url}/services/oauth2/token" self.logger.info(f"SalesforceAPI: Getting API token from {client_url}... ") - response = requests.request( - method="POST", - url=client_url, - data={ - "grant_type": "password", - "client_id": client_id, - "client_secret": client_secret, - "username": username, - "password": password + security_token, - }, - ) + + try: + response = requests.request( + method="POST", + url=client_url, + data={ + "grant_type": "password", + "client_id": client_id, + "client_secret": client_secret, + "username": username, + "password": password + security_token, + }, + ) + except DNSError as error_message: + self.logger.info(f"Network error or DNS resolution failed: {error_message}") + raise ApiException( + cause="Network error or DNS resolution failed. Please check the domain entered", + assistance="Network error or DNS resolution failed. Please check the domain entered", + status_code=400, + data="Network error or DNS resolution failed. Please check the domain entered", + ) if 400 <= response.status_code <= 504: decoded_response = response.content.decode() @@ -357,6 +368,7 @@ def get_error(self, response: str) -> Tuple[str, str, int]: "invalid_grant": "Invalid password or security token supplied.", "invalid_client_id": "Invalid client ID supplied.", "invalid_client": "Invalid client secret supplied.", + "unsupported_grant_type": "Grant type not supported, Please ensure correct login URL is provided", } try: diff --git a/plugins/salesforce/plugin.spec.yaml b/plugins/salesforce/plugin.spec.yaml index 3f7f7c1f20..0fb82f7063 100644 --- a/plugins/salesforce/plugin.spec.yaml +++ b/plugins/salesforce/plugin.spec.yaml @@ -4,7 +4,7 @@ products: [insightconnect] name: salesforce title: Salesforce description: "[Salesforce](https://www.salesforce.com) is a CRM solution that brings together all customer information in a single, integrated platform that enables building a customer-centered business from marketing right through to sales, customer service and business analysis. The Salesforce plugin allows you to search, update, and manage salesforce records. This plugin utilizes the [Salesforce API](https://developer.salesforce.com/docs/atlas.en-us.216.0.api_rest.meta/api_rest/intro_what_is_rest_api.htm)" -version: 2.1.12 +version: 2.1.13 connection_version: 2 vendor: rapid7 support: community @@ -13,7 +13,7 @@ status: [] supported_versions: ["Salesforce API v58 2023-06-30"] sdk: type: full - version: 6.2.0 + version: 6.2.4 user: nobody resources: source_url: https://github.com/rapid7/insightconnect-plugins/tree/master/plugins/salesforce @@ -37,6 +37,7 @@ references: - "[Connecting your app to the API](https://developer.salesforce.com/docs/atlas.en-us.216.0.api_rest.meta/api_rest/quickstart.htm)" - "[SOQL](https://developer.salesforce.com/docs/atlas.en-us.216.0.soql_sosl.meta/soql_sosl/sforce_api_calls_soql.htm)" version_history: + - "2.1.13 - Task Monitor Users: improve error response to UI | Bump SDK to 6.2.4" - "2.1.12 - Task Monitor Users: ensure datetime includes microseconds | Bump SDK to 6.2.0" - "2.1.11 - Task Monitor Users: Return 500 for retry your request error | Bump SDK to 6.1.4" - "2.1.10 - Set Monitor Users task output length | Fix to remove whitespace from connection inputs" diff --git a/plugins/salesforce/setup.py b/plugins/salesforce/setup.py index 2a75dec2e4..a0b42b82db 100755 --- a/plugins/salesforce/setup.py +++ b/plugins/salesforce/setup.py @@ -2,13 +2,14 @@ from setuptools import setup, find_packages -setup(name="salesforce-rapid7-plugin", - version="2.1.12", - description="[Salesforce](https://www.salesforce.com) is a CRM solution that brings together all customer information in a single, integrated platform that enables building a customer-centered business from marketing right through to sales, customer service and business analysis. The Salesforce plugin allows you to search, update, and manage salesforce records. This plugin utilizes the [Salesforce API](https://developer.salesforce.com/docs/atlas.en-us.216.0.api_rest.meta/api_rest/intro_what_is_rest_api.htm)", - author="rapid7", - author_email="", - url="", - packages=find_packages(), - install_requires=['insightconnect-plugin-runtime'], # Add third-party dependencies to requirements.txt, not here! - scripts=['bin/komand_salesforce'] - ) +setup( + name="salesforce-rapid7-plugin", + version="2.1.13", + description="[Salesforce](https://www.salesforce.com) is a CRM solution that brings together all customer information in a single, integrated platform that enables building a customer-centered business from marketing right through to sales, customer service and business analysis. The Salesforce plugin allows you to search, update, and manage salesforce records. This plugin utilizes the [Salesforce API](https://developer.salesforce.com/docs/atlas.en-us.216.0.api_rest.meta/api_rest/intro_what_is_rest_api.htm)", + author="rapid7", + author_email="", + url="", + packages=find_packages(), + install_requires=["insightconnect-plugin-runtime"], # Add third-party dependencies to requirements.txt, not here! + scripts=["bin/komand_salesforce"], +) diff --git a/plugins/salesforce/unit_test/inputs/connection_unsupported_grant.json.inp b/plugins/salesforce/unit_test/inputs/connection_unsupported_grant.json.inp new file mode 100644 index 0000000000..dcda3809a3 --- /dev/null +++ b/plugins/salesforce/unit_test/inputs/connection_unsupported_grant.json.inp @@ -0,0 +1,13 @@ +{ + "clientId": "unsupported-grant-type", + "clientSecret": { + "secretKey": "example-secret-key" + }, + "salesforceAccountUsernameAndPassword": { + "username": "example-username", + "password": "example-password" + }, + "securityToken": { + "secretKey": "example-secret-key" + } +} \ No newline at end of file diff --git a/plugins/salesforce/unit_test/responses/unsupported_grant_type.json.resp b/plugins/salesforce/unit_test/responses/unsupported_grant_type.json.resp new file mode 100644 index 0000000000..a27b8aa5bb --- /dev/null +++ b/plugins/salesforce/unit_test/responses/unsupported_grant_type.json.resp @@ -0,0 +1,4 @@ +{ + "error": "unsupported_grant_type", + "error_description": "unsupported_grant_type" +} \ No newline at end of file diff --git a/plugins/salesforce/unit_test/test_connection.py b/plugins/salesforce/unit_test/test_connection.py index 51624730ae..c695f8ec45 100644 --- a/plugins/salesforce/unit_test/test_connection.py +++ b/plugins/salesforce/unit_test/test_connection.py @@ -66,6 +66,12 @@ def test_connection( "Salesforce error: 'retry your request'", PluginException.assistances[PluginException.Preset.UNKNOWN], ], + [ + "unsupported_grant", + Util.read_file_to_dict("inputs/connection_unsupported_grant.json.inp"), + "Salesforce error: 'Grant type not supported, Please ensure correct login URL is provided'", + PluginException.assistances[PluginException.Preset.INVALID_CREDENTIALS], + ], ] ) def test_connection_raise_exception( diff --git a/plugins/salesforce/unit_test/test_monitor_users.py b/plugins/salesforce/unit_test/test_monitor_users.py index 9980a7ea5e..990e950079 100644 --- a/plugins/salesforce/unit_test/test_monitor_users.py +++ b/plugins/salesforce/unit_test/test_monitor_users.py @@ -11,6 +11,7 @@ from jsonschema import validate from komand_salesforce.tasks.monitor_users.schema import MonitorUsersOutput from komand_salesforce.tasks.monitor_users.task import MonitorUsers +from komand_salesforce.connection.schema import Input from parameterized import parameterized from util import Util @@ -179,3 +180,47 @@ def test_default_cut_off_values_of_7_days(self, mock_unset, mocked_logger, _mock self.assertEqual(user_login_cutoff, mocked_logger.call_args_list[3][0][0]) mock_unset.assert_called() + + @parameterized.expand( + [ + [ + "without_state", + { + "last_user_update_collection_timestamp": "2025-07-21 15:21:15.340262+00:00", + "next_user_collection_timestamp": "2023-07-21 15:21:15.340262+00:00", + "next_user_login_collection_timestamp": "2023-07-20 15:21:15.340262+00:00", + "last_user_login_collection_timestamp": "2023-07-20 14:21:15.340262+00:00", + }, + {}, + ], + ] + ) + def test_bad_domain_provided( + self, + mocked_unset: MagicMock, + mocked_logger: MagicMock, + _mock_request: MagicMock, + _mock_get_time: MagicMock, + test_name: str, + current_state: Dict[str, Any], + expected: Dict[str, Any], + ) -> None: + + params = { + Input.CLIENTID: "example-client-id", + Input.CLIENTSECRET: {"secretKey": "example-secret-key"}, + Input.SALESFORCEACCOUNTUSERNAMEANDPASSWORD: { + "username": "example-username", + "password": "example-password", + }, + Input.SECURITYTOKEN: {"secretKey": "example-secret-key"}, + Input.LOGINURL: "bad_domain", + } + + self.action = Util.default_connector(MonitorUsers(), params=params) + actual, _actual_state, _has_more_pages, status_code, error = self.action.run(state=current_state) + + self.assertEqual(error.cause, "Network error or DNS resolution failed. Please check the domain entered") + self.assertEqual(status_code, 400) + + validate(actual, MonitorUsersOutput.schema) diff --git a/plugins/salesforce/unit_test/util.py b/plugins/salesforce/unit_test/util.py index 7190b88068..c0fa8de356 100644 --- a/plugins/salesforce/unit_test/util.py +++ b/plugins/salesforce/unit_test/util.py @@ -64,6 +64,11 @@ def json(self): url = kwargs.get("url", "") method = kwargs.get("method", "") + if url == "https://bad_domain/services/oauth2/token": + from requests.exceptions import ConnectionError as DNSError + + raise DNSError() + if url == "https://login.salesforce.com/services/oauth2/token": if data.get("client_id") == "invalid-client-id": return MockResponse(400, "invalid_grant") # returns 400 when failing to get a token. @@ -73,6 +78,8 @@ def json(self): return MockResponse(400, "invalid_client_id") if data.get("client_id") == "retry-request": return MockResponse(400, "retry_request") + if data.get("client_id") == "unsupported-grant-type": + return MockResponse(400, "unsupported_grant_type") return MockResponse(200, "get_token") if url == "https://example.com/services/data/": return MockResponse(200, "get_version")