From cf81db26d252fbd48865643b3ea09d8f8108654e Mon Sep 17 00:00:00 2001 From: nmccullagh-r7 Date: Fri, 8 Nov 2024 15:10:24 +0000 Subject: [PATCH] [Palo Alto] Return 401 correctly --- plugins/palo_alto_cortex_xdr/.CHECKSUM | 6 +++--- plugins/palo_alto_cortex_xdr/Dockerfile | 2 +- .../bin/icon_palo_alto_cortex_xdr | 2 +- plugins/palo_alto_cortex_xdr/help.md | 1 + .../tasks/monitor_alerts/task.py | 11 ++++++++++- .../icon_palo_alto_cortex_xdr/util/api.py | 14 ++++++++++++-- plugins/palo_alto_cortex_xdr/plugin.spec.yaml | 5 +++-- plugins/palo_alto_cortex_xdr/requirements.txt | 3 ++- plugins/palo_alto_cortex_xdr/setup.py | 2 +- .../unit_test/{mock.py => mock_helper.py} | 0 .../unit_test/test_get_query_results.py | 2 +- .../unit_test/test_monitor_alerts.py | 18 ++++++++++++++++++ 12 files changed, 53 insertions(+), 13 deletions(-) rename plugins/palo_alto_cortex_xdr/unit_test/{mock.py => mock_helper.py} (100%) diff --git a/plugins/palo_alto_cortex_xdr/.CHECKSUM b/plugins/palo_alto_cortex_xdr/.CHECKSUM index ecba0eb240..9004d2c8d6 100644 --- a/plugins/palo_alto_cortex_xdr/.CHECKSUM +++ b/plugins/palo_alto_cortex_xdr/.CHECKSUM @@ -1,7 +1,7 @@ { - "spec": "5f65f97ed0704bd87cb78e24eb9dc1b3", - "manifest": "094c90db12918a2d28277d8b94124397", - "setup": "67c9748687eb5d9ea0eccfccb53610e1", + "spec": "1a737630103c5a3fb2d61444c2fefbb9", + "manifest": "58618c879c00000568c7d1e4da6bc0a1", + "setup": "cb9fd1212032e1f3d1d0246bf663c090", "schemas": [ { "identifier": "allow_file/schema.py", diff --git a/plugins/palo_alto_cortex_xdr/Dockerfile b/plugins/palo_alto_cortex_xdr/Dockerfile index 6fafead48d..721f85e435 100755 --- a/plugins/palo_alto_cortex_xdr/Dockerfile +++ b/plugins/palo_alto_cortex_xdr/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=linux/amd64 rapid7/insightconnect-python-3-slim-plugin:6.1.4 +FROM --platform=linux/amd64 rapid7/insightconnect-python-3-slim-plugin:6.2.0 LABEL organization=rapid7 LABEL sdk=python diff --git a/plugins/palo_alto_cortex_xdr/bin/icon_palo_alto_cortex_xdr b/plugins/palo_alto_cortex_xdr/bin/icon_palo_alto_cortex_xdr index 61b80408fa..63eb79421e 100755 --- a/plugins/palo_alto_cortex_xdr/bin/icon_palo_alto_cortex_xdr +++ b/plugins/palo_alto_cortex_xdr/bin/icon_palo_alto_cortex_xdr @@ -6,7 +6,7 @@ from sys import argv Name = "Palo Alto Cortex XDR" Vendor = "rapid7" -Version = "4.0.3" +Version = "4.0.4" Description = "Stop modern attacks with the industry's first extended detection and response platform that spans your endpoints, network and cloud data" diff --git a/plugins/palo_alto_cortex_xdr/help.md b/plugins/palo_alto_cortex_xdr/help.md index 1f2a247d4e..84b7f16c78 100644 --- a/plugins/palo_alto_cortex_xdr/help.md +++ b/plugins/palo_alto_cortex_xdr/help.md @@ -927,6 +927,7 @@ Isolate Endpoint fails with 500 error - This will happen if an isolation action # Version History +* 4.0.4 - Raise authentication errors if provided invalid credentials * 4.0.3 - `Monitor Incidents` - Add custom config exception handling * 4.0.2 - SDK bump to 6.1.4 * 4.0.1 - SDK Bump to 6.1.3 diff --git a/plugins/palo_alto_cortex_xdr/icon_palo_alto_cortex_xdr/tasks/monitor_alerts/task.py b/plugins/palo_alto_cortex_xdr/icon_palo_alto_cortex_xdr/tasks/monitor_alerts/task.py index 5b986d666d..ebdb989687 100644 --- a/plugins/palo_alto_cortex_xdr/icon_palo_alto_cortex_xdr/tasks/monitor_alerts/task.py +++ b/plugins/palo_alto_cortex_xdr/icon_palo_alto_cortex_xdr/tasks/monitor_alerts/task.py @@ -8,7 +8,7 @@ Component, ) from datetime import datetime, timedelta, timezone -from insightconnect_plugin_runtime.exceptions import PluginException +from insightconnect_plugin_runtime.exceptions import PluginException, APIException from insightconnect_plugin_runtime.helper import hash_sha1 from typing import Any, Dict, Tuple, Union, Optional @@ -61,6 +61,15 @@ def run(self, params={}, state={}, custom_config: dict = {}): # pylint: disable return response, state, has_more_pages, 200, None + except APIException as error: + return ( + [], + {}, + False, + error.status_code, + PluginException(data=error, cause=error.cause, assistance=error.assistance), + ) + except PluginException as error: if isinstance(error.data, Response): # if there is response data in the error then use it in the exception diff --git a/plugins/palo_alto_cortex_xdr/icon_palo_alto_cortex_xdr/util/api.py b/plugins/palo_alto_cortex_xdr/icon_palo_alto_cortex_xdr/util/api.py index 50e18f51cf..b8dc56691a 100644 --- a/plugins/palo_alto_cortex_xdr/icon_palo_alto_cortex_xdr/util/api.py +++ b/plugins/palo_alto_cortex_xdr/icon_palo_alto_cortex_xdr/util/api.py @@ -16,6 +16,7 @@ ConnectionTestException, ResponseExceptionData, HTTPStatusCodes, + APIException, ) from insightconnect_plugin_runtime.helper import extract_json, make_request from requests import Response @@ -473,12 +474,21 @@ def _handle_401(self, response: Response, url: str, post_body: dict): re-run the creds generation """ - if response.status_code == 401: - self.logger.info(f"Received HTTP {response.status_code}, re-authenticating to Palo Alto Cortex XDR") + if response.status_code in [401, 403]: + self.logger.info(f"Received HTTP {response.status_code}, attempting to refresh token") new_headers = self._advanced_authentication(api_key=self.api_key, api_key_id=self.api_key_id) new_response = self.build_request(url=url, headers=new_headers, post_body=post_body) + if new_response.status_code in [401, 403]: + self.logger.info("Token is still invalid, raising exception") + raise APIException( + cause=PluginException.causes.get(PluginException.Preset.API_KEY), + assistance=PluginException.assistances.get(PluginException.Preset.API_KEY), + status_code=401, + data=response.text, + ) + return new_response else: diff --git a/plugins/palo_alto_cortex_xdr/plugin.spec.yaml b/plugins/palo_alto_cortex_xdr/plugin.spec.yaml index e66ac6eda7..f262787174 100644 --- a/plugins/palo_alto_cortex_xdr/plugin.spec.yaml +++ b/plugins/palo_alto_cortex_xdr/plugin.spec.yaml @@ -4,12 +4,12 @@ products: [insightconnect] name: palo_alto_cortex_xdr title: Palo Alto Cortex XDR description: Stop modern attacks with the industry's first extended detection and response platform that spans your endpoints, network and cloud data -version: 4.0.3 +version: 4.0.4 connection_version: 2 cloud_ready: true sdk: type: slim - version: 6.1.4 + version: 6.2.0 user: nobody supported_versions: ["2024-07-15 Palo Alto Cortex XDR API"] vendor: rapid7 @@ -38,6 +38,7 @@ key_features: - "Add files to the block or allow lists" troubleshooting: "Isolate Endpoint fails with 500 error - This will happen if an isolation action (Isolate or Unisolate) is in progress on the selected endpoint. Wait a few minutes and try again." version_history: + - "4.0.4 - Raise authentication errors if provided invalid credentials" - "4.0.3 - `Monitor Incidents` - Add custom config exception handling" - "4.0.2 - SDK bump to 6.1.4" - "4.0.1 - SDK Bump to 6.1.3" diff --git a/plugins/palo_alto_cortex_xdr/requirements.txt b/plugins/palo_alto_cortex_xdr/requirements.txt index 44267169c8..9bc067d316 100755 --- a/plugins/palo_alto_cortex_xdr/requirements.txt +++ b/plugins/palo_alto_cortex_xdr/requirements.txt @@ -1,6 +1,7 @@ # List third-party dependencies here, separated by newlines. # All dependencies must be version-pinned, eg. requests==1.2.0 # See: https://pip.pypa.io/en/stable/user_guide/#requirements-files -parameterized==0.8.1 +parameterized==0.9.0 timeout-decorator==0.5.0 freezegun==1.5.1 +timeout_decorator==0.5.0 diff --git a/plugins/palo_alto_cortex_xdr/setup.py b/plugins/palo_alto_cortex_xdr/setup.py index b83aa613b6..d31f400a7e 100755 --- a/plugins/palo_alto_cortex_xdr/setup.py +++ b/plugins/palo_alto_cortex_xdr/setup.py @@ -3,7 +3,7 @@ setup(name="palo_alto_cortex_xdr-rapid7-plugin", - version="4.0.3", + version="4.0.4", description="Stop modern attacks with the industry's first extended detection and response platform that spans your endpoints, network and cloud data", author="rapid7", author_email="", diff --git a/plugins/palo_alto_cortex_xdr/unit_test/mock.py b/plugins/palo_alto_cortex_xdr/unit_test/mock_helper.py similarity index 100% rename from plugins/palo_alto_cortex_xdr/unit_test/mock.py rename to plugins/palo_alto_cortex_xdr/unit_test/mock_helper.py diff --git a/plugins/palo_alto_cortex_xdr/unit_test/test_get_query_results.py b/plugins/palo_alto_cortex_xdr/unit_test/test_get_query_results.py index 048c78f8e9..d4257a9d6a 100644 --- a/plugins/palo_alto_cortex_xdr/unit_test/test_get_query_results.py +++ b/plugins/palo_alto_cortex_xdr/unit_test/test_get_query_results.py @@ -11,7 +11,7 @@ from icon_palo_alto_cortex_xdr.triggers.get_query_results import GetQueryResults from icon_palo_alto_cortex_xdr.triggers.get_query_results.schema import Input -from mock import mock_request_200 +from mock_helper import mock_request_200 from util import MockTrigger, Util diff --git a/plugins/palo_alto_cortex_xdr/unit_test/test_monitor_alerts.py b/plugins/palo_alto_cortex_xdr/unit_test/test_monitor_alerts.py index 23809875c9..5660759a4a 100644 --- a/plugins/palo_alto_cortex_xdr/unit_test/test_monitor_alerts.py +++ b/plugins/palo_alto_cortex_xdr/unit_test/test_monitor_alerts.py @@ -13,6 +13,7 @@ from taskutil import TaskUtil, mock_conditions, mocked_response_type from insightconnect_plugin_runtime.exceptions import PluginException from typing import Union +from mock_helper import MockResponse STUB_STATE_EXPECTED_SECOND_PAGE = { "current_count": 2, @@ -141,6 +142,16 @@ def test_monitor_alerts_pagination( ), 402, ], + [ + "Unauthorized", + {}, + PluginException( + cause=PluginException.causes.get(PluginException.Preset.API_KEY), + assistance=PluginException.assistances.get(PluginException.Preset.API_KEY), + data='An error occurred during plugin execution!\n\nInvalid API key provided. Verify your API key configured in your connection is correct. Response was: {"reply": {"err_code": 401, "err_msg": "Public API request unauthorized", "err_extra": null}}', + ), + 401, + ], [ "Forbidden", STUB_STATE_ERROR, @@ -179,6 +190,13 @@ def test_monitor_alerts_error_handling( if error_code == 500: mocked_response = mock_conditions(200, file_name="monitor_alerts_faulty_response") + elif error_code == 401: + mocked_response = MockResponse( + filename="monitor_alerts_faulty_response", + status_code=401, + text='{"reply": {"err_code": 401, "err_msg": "Public API request unauthorized", "err_extra": null}}', + ) + # This else applies to every other usual exception else: mocked_response = mocked_response_type(error_code)