From cc652ef21e540f75403be819698e6ff856edfd2a Mon Sep 17 00:00:00 2001 From: Chirag Wadhwani Date: Wed, 5 Jul 2023 18:17:15 +0530 Subject: [PATCH 1/4] ThreatConnect: Created a debug build for Threatconnect with 1.0.0-debug version --- threat_connect/CHANGELOG.md | 4 + threat_connect/__init__.py | 1 + threat_connect/icon.png | Bin 0 -> 64031 bytes threat_connect/main.py | 1062 ++++++++++++++++++++++++++++++++++ threat_connect/manifest.json | 100 ++++ 5 files changed, 1167 insertions(+) create mode 100644 threat_connect/CHANGELOG.md create mode 100644 threat_connect/__init__.py create mode 100644 threat_connect/icon.png create mode 100644 threat_connect/main.py create mode 100644 threat_connect/manifest.json diff --git a/threat_connect/CHANGELOG.md b/threat_connect/CHANGELOG.md new file mode 100644 index 0000000..ee497eb --- /dev/null +++ b/threat_connect/CHANGELOG.md @@ -0,0 +1,4 @@ +# 1.0.0 +## Added +- Initial Release + diff --git a/threat_connect/__init__.py b/threat_connect/__init__.py new file mode 100644 index 0000000..f43410c --- /dev/null +++ b/threat_connect/__init__.py @@ -0,0 +1 @@ +"""Threat Connect Plugin Package""" diff --git a/threat_connect/icon.png b/threat_connect/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..6b632a9c5ebaab5cf853ccb559442b53caa3c277 GIT binary patch literal 64031 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 z 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 PKtJ-*%2G8F#=-vwNR{cB literal 0 HcmV?d00001 diff --git a/threat_connect/main.py b/threat_connect/main.py new file mode 100644 index 0000000..672cab6 --- /dev/null +++ b/threat_connect/main.py @@ -0,0 +1,1062 @@ +""" +BSD 3-Clause License. + +Copyright (c) 2021, Netskope OSS +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +ThreatConnect Plugin implementation to pull the data from +ThreatConnect Platform. +""" + +import datetime +import time +import hmac +import hashlib +import base64 +import requests +import urllib.parse +from typing import Dict, List +from netskope.integrations.cte.plugin_base import ( + PluginBase, + ValidationResult, + PushResult, +) +from netskope.integrations.cte.utils import TagUtils +from netskope.integrations.cte.models import ( + Indicator, + IndicatorType, + SeverityType, + TagIn, +) +from netskope.common.utils import add_user_agent +from netskope.integrations.cte.models.business_rule import ( + ActionWithoutParams, + Action, +) + +THREATCONNECT_TO_INTERNAL_TYPE = { + "sha256": IndicatorType.SHA256, + "md5": IndicatorType.MD5, + "url": IndicatorType.URL, +} + +RATING_TO_SEVERITY = { + 0: SeverityType.UNKNOWN, + 1: SeverityType.LOW, + 2: SeverityType.LOW, + 3: SeverityType.MEDIUM, + 4: SeverityType.HIGH, + 5: SeverityType.CRITICAL, +} + +SEVERITY_TO_RATING = { + SeverityType.UNKNOWN: 0, + SeverityType.LOW: 1, + SeverityType.MEDIUM: 3, + SeverityType.HIGH: 4, + SeverityType.CRITICAL: 5, +} + +LIMIT = 1000 # Maximum Response LIMIT at Time. +TAG_NAME = "Netskope CE" + + +class ThreatConnectPlugin(PluginBase): + """ThreatConnect Plugin Base Class. + + Args: + PluginBase (PluginBase): Inherit PluginBase Class from Cloud + Threat Exchange Integration. + """ + + def handle_error(self, resp) -> Dict: + """Handle the different HTTP response code. + + Args: + resp (requests.models.Response): Response object returned from API. + Returns: + dict: Returns the dictionary of response JSON when response is 200. + Raises: + HTTPError: When the response code is not 200. + """ + if resp.status_code == 200 or resp.status_code == 201: + try: + return resp.json() + except ValueError: + self.logger.error( + "Plugin: ThreatConnect, " + "Exception occurred while parsing JSON response." + ) + elif resp.status_code == 401: + self.logger.error( + "Plugin: ThreatConnect, Received exit code 401, " + "Authentication Error." + ) + elif resp.status_code == 403: + self.logger.error( + "Plugin: ThreatConnect, " + "Received exit code 403, Forbidden User." + ) + elif resp.status_code >= 400 and resp.status_code < 500: + self.logger.error( + f"Plugin: ThreatConnect, " + f"Received exit code {resp.status_code}, HTTP client Error." + ) + elif resp.status_code >= 500 and resp.status_code < 600: + self.logger.error( + f"Plugin: ThreatConnect, " + f"Received exit code {resp.status_code}, HTTP server Error." + ) + else: + self.logger.error( + f"Plugin: ThreatConnect, " + f"Received exit code {resp.status_code}, HTTP Error." + ) + resp.raise_for_status() + + def _get_headers_for_auth( + self, api_path: str, access_id: str, secret_key: str, request_type: str + ) -> Dict: + """Return header for authentication. + + Args: + api_path (str): API path string. + access_id(str): ThreatConnect API Access ID. + secret_key(str): ThreatConnect API Secret Key. + request_type (str): Request Type like GET, POST, PUT, etc. + + Returns: + header (dict) : Header for authentication. + """ + unix_epoch_time = int(time.time()) + api_path = f"{api_path}:{request_type}:{unix_epoch_time}" + bytes_api_path = bytes(api_path, "utf-8") + bytes_secret_key = bytes(secret_key, "utf-8") + + # HMAC-SHA256 + dig = hmac.new( + bytes_secret_key, msg=bytes_api_path, digestmod=hashlib.sha256 + ).digest() + + # BASE64 ENCODE + hmac_sha256 = base64.b64encode(dig).decode() + signature = f"TC {access_id}:{hmac_sha256}" + header = { + "Authorization": str(signature), + "Timestamp": str(unix_epoch_time), + } + return header + + def get_reputation(self, ioc_response_json) -> int: + """Get reputation value based on confidence score. + + Args: + ioc_response_json (json): Single response JSON object. + + Returns: + int: Reputation score ( >= 1 and <= 10). + """ + # confidence is in between 0 and 100 + # reputation is in between 0 and 10. + if "confidence" in ioc_response_json: + reputation = ioc_response_json["confidence"] + if reputation and reputation > 10: + return reputation // 10 + else: + return 1 + else: + return 5 # default value + + def get_api_url(self, api_path, threat_type): + """Get API url. + + Args: + api_path (str): API endpoint. + threat_type (str): Type of data to pull. + + Returns: + str: return API url endpoint. + """ + result_start = 0 + if self.last_run_at: + last_run_time = self.last_run_at + else: + last_run_time = datetime.datetime.now() - datetime.timedelta( + days=self.configuration["days"] + ) + last_run_time = last_run_time.strftime("%Y-%m-%dT%H:%M:%SZ")[:10] + query = None + + if threat_type == "Both": + query = 'typeName IN ("File","URL")' + else: + query = f'typeName == "{threat_type}"' + + # adding last run time query + query += f" AND lastModified >= '{last_run_time}'" + + filtered_string = "tql=" + urllib.parse.quote(query) + api_url = f"{api_path}?fields=tags&{filtered_string}" + api_url += f"&resultStart={result_start}&resultLimit={LIMIT}" + return api_url + + def get_pull_request(self, api_url): + """Make pull request to get data from ThreatConnect. + + Args: + api_url (str): API url endpoint. + + Returns: + Response: Return API response. + """ + headers = self._get_headers_for_auth( + api_url, + self.configuration["access_id"], + self.configuration["secret_key"], + "GET", + ) + query_endpoint = self.configuration["base_url"] + api_url + self.logger.info(f"Debug Plugin: Threatconnect, Query endpoint: {query_endpoint}") + self.logger.info(f"Debug Plugin: Threatconnect, Headers: {headers}") + ioc_response = self._api_calls( + requests.get( + query_endpoint, + headers=add_user_agent(headers), + proxies=self.proxy, + verify=self.ssl_validation, + ), + ) + return ioc_response + + def make_indicators(self, ioc_response_json, tagging, indicator_list): + """Add received data to Netskope. + + Args: + ioc_response_json (_type_): _description_ + tagging (_type_): _description_ + indicator_list (_type_): _description_ + """ + + md5, sha256, url_ioc, file, skipped = 0, 0, 0, 0, 0 + for ioc_json in ioc_response_json["data"]: + if ( + "tags" in ioc_json + and "data" in ioc_json["tags"] + and TAG_NAME + not in [ + tag_info["name"] + for tag_info in ioc_json["tags"]["data"] + if "name" in tag_info + ] + ): + if ioc_json["type"] == "File": + file += 1 + if "md5" in ioc_json: + indicator_list.append( + Indicator( + value=ioc_json["md5"].lower(), + type=IndicatorType.MD5, + active=ioc_json.get("active", True), + severity=RATING_TO_SEVERITY[ + ioc_json.get("rating", 0) + ], + reputation=self.get_reputation(ioc_json), + comments=ioc_json.get("description", ""), + firstSeen=ioc_json.get("dateAdded"), + lastSeen=ioc_json.get("lastModified"), + tags=self._create_tags(TagUtils(), ioc_json) + if tagging + else [], + ) + ) + md5 += 1 + + if "sha256" in ioc_json: + indicator_list.append( + Indicator( + value=ioc_json["sha256"].lower(), + type=IndicatorType.SHA256, + active=ioc_json.get("active", True), + severity=RATING_TO_SEVERITY[ + ioc_json.get("rating", 0) + ], + reputation=self.get_reputation(ioc_json), + comments=ioc_json.get("description", ""), + firstSeen=ioc_json.get("dateAdded"), + lastSeen=ioc_json.get("lastModified"), + tags=self._create_tags(TagUtils(), ioc_json) + if tagging + else [], + ) + ) + sha256 += 1 + else: + for url in ioc_json["text"].split(","): + indicator_list.append( + Indicator( + value=url, + type=IndicatorType.URL, + active=ioc_json.get("active", True), + severity=RATING_TO_SEVERITY[ + ioc_json.get("rating", 0) + ], + reputation=self.get_reputation(ioc_json), + comments=ioc_json.get("description", ""), + firstSeen=ioc_json.get("dateAdded"), + lastSeen=ioc_json.get("lastModified"), + tags=self._create_tags(TagUtils(), ioc_json) + if tagging + else [], + ) + ) + url_ioc += 1 + else: + skipped += 1 + + self.logger.info(f"Debug Plugin: Threatconnect, Stats - Total File IOCs: {file}, MD5: {md5}, SHA256: {sha256}, URL: {url_ioc}, skipped: {skipped}") + + def pull_data_from_threatconnect( + self, + api_path: str, + threat_type: str, + tagging: bool, + ) -> List[Indicator]: + """Fetch Data from ThreatConnect API. + + Args: + api_path (str): API endpoint. + threat_type (str): Type of threat data. + tagging (bool): Enable or disable tagging. + + Returns: + List[Indicator]: List of Indicator Models. + """ + indicator_list = [] + self.logger.info(f"Debug Plugin: Threatconnect, Pulling data from threatconnect") + api_url = self.get_api_url(api_path, threat_type) + while True: + ioc_response = self.get_pull_request( + api_url, + ) + self.logger.info(f"Debug Plugin: Threatconnect, API Response code: {ioc_response.status_code}") + ioc_response_json = self.handle_error(ioc_response) + if ioc_response_json.get("status", "") != "Success": + raise requests.exceptions.HTTPError( + f"Plugin: ThreatConnect, Unable to fetch Indicator. " + f"Error: {ioc_response_json.get('message', '', )}." + ) + elif ioc_response_json.get("data", None): + self.logger.info(f"Debug Plugin: Threatconnect, Total data: {len(ioc_response_json.get('data', []))}") + self.make_indicators( + ioc_response_json, + tagging, + indicator_list, + ) + # Handling Result Limit Of API + if ioc_response_json.get("next", None): + self.logger.info(f"Debug Plugin: Threatconnect, The request has next page. We will be querying threatconnect for more indicators") + api_url = ioc_response_json["next"].replace( + self.configuration["base_url"], "" + ) + else: + self.logger.info(f"Debug Plugin: Threatconnect, Total number of indicators returned: {len(indicator_list)}") + return indicator_list + else: + # case: status -> Success, return -> [] + self.logger.info(f"Debug Plugin: Threatconnect, Returning empty list") + return [] + + def _create_tags(self, tag_utils: TagUtils, ioc_response) -> List[str]: + """Create tag list and add tags to netskope using tag_utils. + + Args: + tag_utils (tag_utils): Tag utility class object. + ioc_response (JSON): Indicator response object. + + Returns: + List[str]: List of tag names. + """ + tag_list = [] + try: + if "tags" in ioc_response and ioc_response["tags"] != {}: + for tag_json in ioc_response["tags"]["data"]: + tag_name = tag_json["name"] + if tag_name is not None and not tag_utils.exists( + tag_name.strip() + ): + tag_utils.create_tag( + TagIn(name=tag_name.strip(), color="#ED3347") + ) + tag_list.append(tag_name.strip()) + except KeyError as k: + self.logger.error( + f"Plugin: ThreatConnect, " + f"Key not found for tag utility. Error: {k}" + ) + return tag_list + + def _api_calls(self, request): + """Call API and handle request exception. + + Args: + request(Response): Lambda function to request the API. + + Raises: + HTTPError: When response code is not 200. + Exception + + Returns: + response: Return response from API. + """ + try: + return request + except requests.exceptions.ProxyError as err: + self.logger.error( + "Plugin: ThreatConnect, Invalid proxy configuration." + ) + raise err + except requests.exceptions.ConnectionError as err: + self.logger.error( + f"Plugin: ThreatConnect, Connection Error occurred: {err}." + ) + raise err + except requests.exceptions.RequestException as err: + self.logger.error( + f"Plugin: ThreatConnect, " + f"Request Exception occurred: {err}." + ) + raise err + except Exception as err: + self.logger.error( + f"Plugin: ThreatConnect, Exception occurred: {err}." + ) + raise err + + def _is_valid_credentials( + self, base_url: str, access_id: str, secret_key: str + ) -> bool: + """Validate credentials. + + Args: + access_id (str): Access ID for ThreatConnect. + secret_key (str): Secret Key for ThreatConnect. + + Returns: + bool: True for valid credentials and false for not valid. + """ + api_path = "/api/v3/security/owners" + query_endpoint = base_url + api_path + headers = self._get_headers_for_auth( + api_path, + access_id, + secret_key, + "GET", + ) + response = self._api_calls( + requests.get( + query_endpoint, + headers=add_user_agent(headers), + proxies=self.proxy, + verify=self.ssl_validation, + ), + ) + + if response.status_code == 200 or response.status_code == 201: + return True + else: + return False + + def _validate_url(self, url: str) -> bool: + """Validate the URL using parsing. + + Args: + url (str): Given URL. + + Returns: + bool: True or False { Valid or not Valid URL }. + """ + parsed = urllib.parse.urlparse(url.strip()) + return ( + parsed.scheme.strip() != "" + and parsed.netloc.strip() != "" + and (parsed.path.strip() == "/" or parsed.path.strip() == "") + ) + + def validate(self, configuration: Dict) -> ValidationResult: + """Validate the configuration. + + Args: + configuration(dict): Configuration from manifest.json. + + Returns: + ValidationResult: Valid configuration fields or not. + """ + # Base URL + if ( + "base_url" not in configuration + or not isinstance(configuration["base_url"], str) + or not configuration["base_url"].strip() + or not self._validate_url(configuration["base_url"]) + or "threatconnect" not in configuration["base_url"].split(".") + ): + self.logger.error( + "ThreatConnect Plugin: " + "Invalid Base URL found in the configuration parameters." + ) + return ValidationResult( + success=False, + message="Invalid Base URL provided.", + ) + # Access_ID + if ( + "access_id" not in configuration + or not isinstance(configuration["access_id"], str) + or not configuration["access_id"].strip() + ): + self.logger.error( + "ThreatConnect Plugin: " + "Invalid Access ID found in the configuration parameters." + ) + return ValidationResult( + success=False, + message="Invalid Access ID provided.", + ) + # Secret Key + if ( + "secret_key" not in configuration + or not isinstance(configuration["secret_key"], str) + or not configuration["secret_key"].strip() + ): + self.logger.error( + "ThreatConnect Plugin: " + "No Secret key found in configuration parameters." + ) + return ValidationResult( + success=False, message="Invalid Secret key provided." + ) + # Enable Tagging + if "enable_tagging" not in configuration or configuration[ + "enable_tagging" + ] not in ["Yes", "No"]: + self.logger.error( + "ThreatConnect Plugin: " + "Value of Enable Tagging should be 'Yes' or 'No'." + ) + return ValidationResult( + success=False, + message="Invalid value for 'Enable Tagging' provided." + "Allowed values are 'Yes', or 'No'.", + ) + # Enable Polling + if "is_pull_required" not in configuration or configuration[ + "is_pull_required" + ] not in ["Yes", "No"]: + self.logger.error( + "ThreatConnect Plugin: " + "Value of Enable Polling should be 'Yes' or 'No'." + ) + return ValidationResult( + success=False, + message="Invalid value for 'Enable Polling' provided." + "Allowed values are 'Yes', or 'No'.", + ) + if not self._is_valid_credentials( + configuration["base_url"], + configuration["access_id"], + configuration["secret_key"], + ): + return ValidationResult( + success=False, + message="Invalid Access ID or Secret key provided.", + ) + # Initial Range + try: + if ( + "days" not in configuration + or not configuration["days"] + or int(configuration["days"]) <= 0 + or int(configuration["days"]) > 365 + ): + self.logger.error( + "ThreatConnect Plugin: " + "Validation error occurred Error: " + "Invalid Initial Range provided." + ) + return ValidationResult( + success=False, + message="Invalid Initial Range provided.", + ) + except ValueError: + return ValidationResult( + success=False, + message="Invalid Initial Range provided.", + ) + else: + return ValidationResult( + success=True, + message="Validation successful.", + ) + + def pull(self) -> List[Indicator]: + """Pull Indicators data from ThreatConnect API. + + Returns: + List[Indicator] : Return List of Indicators Models. + """ + if self.configuration["is_pull_required"] == "Yes": + self.logger.info("Plugin: ThreatConnect Polling is enabled.") + api_path = "/api/v3/indicators" + if self.configuration["enable_tagging"] == "Yes": + tagging = True + else: + tagging = False + return self.pull_data_from_threatconnect( + api_path, + self.configuration["threat_type"], + tagging, + ) + else: + self.logger.info( + "Plugin: ThreatConnect Polling is disabled, skipping." + ) + return [] + + def get_group_id(self, action_dict): + """Return group id based on condition. + + Args: + action_dict (Dict): Aciton dictionary. + + Returns: + str: Return group id. + """ + if action_dict.get("parameters").get("group_name") != "create_group": + return action_dict.get("parameters")["group_name"] + else: + # Creating New Group + api_path = "/api/v3/groups/" + create_group_api = self.configuration["base_url"] + api_path + headers = self._get_headers_for_auth( + api_path, + self.configuration["access_id"], + self.configuration["secret_key"], + "POST", + ) + group_names = self.get_group_names() + if ( + action_dict.get("parameters")["new_group_name"].strip() + not in group_names + ): + data = { + "name": action_dict.get("parameters")["new_group_name"].strip(), + "type": action_dict.get("parameters")["new_group_type"], + "tags": { + "data": [ + {"name": TAG_NAME}, + ] + }, + } + response = self._api_calls( + requests.post( + create_group_api, + headers=add_user_agent(headers), + json=data, + proxies=self.proxy, + verify=self.ssl_validation, + ) + ) + if ( + response.json()["status"] == "Success" + and "data" in response.json() + and "name" in response.json()["data"] + and "id" in response.json()["data"] + ): + action_dict.get("parameters")[ + "group_name" + ] = response.json()["data"]["name"] + return response.json()["data"]["id"] + else: + self.logger.error( + f"Error while creating a group. " + f"Error: {response.json()['message']}" + ) + else: + return group_names[ + action_dict.get("parameters")["new_group_name"] + ] + + def prepare_payload(self, indicator, existing_group_id): + """Prepare payload for request. + + Args: + indicator (Indicator): given indicators. + existing_group_id (_type_): group id. + + Returns: + Dict: return dictionary of data. + """ + data = {} + if ( + indicator.type == IndicatorType.URL + and 1 <= len(indicator.value) <= 500 + ): + data["text"] = indicator.value + data["type"] = "url" + elif indicator.type == IndicatorType.MD5: + data["md5"] = indicator.value + data["type"] = "File" + elif indicator.type == IndicatorType.SHA256: + data["sha256"] = indicator.value + data["type"] = "File" + data["associatedGroups"] = { + "data": [ + { + "id": existing_group_id, + } + ] + } + data["tags"] = {"data": [{"name": TAG_NAME}]} + data["rating"] = SEVERITY_TO_RATING[indicator.severity] + data["confidence"] = indicator.reputation * 10 + return data + + def update_ioc(self, value, group_id): + """Update IoCs metadata for mutiple groups. + + Args: + value (str): value of IoC + group_id (str): group id + + Returns: + Response: return Response object. + """ + api_path = f"/api/v3/indicators/{value}" + url = self.configuration["base_url"] + api_path + headers = self._get_headers_for_auth( + api_path, + self.configuration["access_id"], + self.configuration["secret_key"], + "PUT", + ) + update_data = { + "associatedGroups": { + "data": [ + {"id": group_id}, + ], + "mode": "append", + }, + } + response = self._api_calls( + requests.put( + url, + headers=add_user_agent(headers), + json=update_data, + proxies=self.proxy, + verify=self.ssl_validation, + ) + ) + return response + + def push(self, indicators: List[Indicator], action_dict: Dict): + """Push Indicators to ThreatConnect Platform. + + Args: + indicators (List[Indicator]): List of Indicators to push. + action_dict (Dict): action dictionary for performing actions. + + Returns: + PushResult: return PushResult object with success and message + parameters. + """ + existing_group_id = self.get_group_id(action_dict) + api_path = "/api/v3/indicators" + query_endpoint = self.configuration["base_url"] + api_path + invalid_ioc = 0 + already_exists = 0 + for indicator in indicators: + if ( + indicator.type == IndicatorType.URL + and len(indicator.value) > 500 + ): + invalid_ioc += 1 + continue + data = self.prepare_payload(indicator, existing_group_id) + headers = self._get_headers_for_auth( + api_path, + self.configuration["access_id"], + self.configuration["secret_key"], + "POST", + ) + response = self._api_calls( + requests.post( + query_endpoint, + headers=add_user_agent(headers), + json=data, + proxies=self.proxy, + verify=self.ssl_validation, + ) + ) + if ( + response.json()["status"] == "Success" + and response.json()["message"] == "Created" + ): + continue + elif response.json()["message"].endswith("already exists"): + response = self.update_ioc( + indicator.value.upper(), + data["associatedGroups"]["data"][0]["id"], + ) + if response.json()["status"] == "Success": + already_exists += 1 + else: + self.logger.error( + f"Error while updating indicator metadata. " + f"Error: {response.json()['message']}." + ) + elif ( + response.json()["message"].startswith("Please enter a valid") + or response.json()["message"] + == "This Indicator is contained on a " + "system-wide exclusion list." + ): + invalid_ioc += 1 + else: + self.logger.error( + f"Error while pushing IoCs to ThreatConnect. " + f"Error: {response.json()['message']}." + ) + return PushResult( + success=False, + message="Error while pushing IoCs to ThreatConnect.", + ) + if invalid_ioc != 0: + self.logger.error( + f"Skipping {invalid_ioc} invalid IoCs while pushing to " + f"ThreatConnect." + ) + if already_exists != 0: + self.logger.warn( + f"Updated {already_exists} IoC(s) on ThreatConnect." + ) + return PushResult( + success=True, + message="Indicators pushed successfully to ThreatConnect.", + ) + + def get_actions(self) -> List[ActionWithoutParams]: + """Get available Actions. + + Returns: + List[ActionWithoutParams]: Return list of actions. + """ + return [ + ActionWithoutParams(label="Add to Group", value="add_to_group") + ] + + def get_owner(self): + """Get owner information from given API credentials. + + Returns: + str: Name of owner. + """ + api_path = "/api/v2/owners/mine" + headers = self._get_headers_for_auth( + api_path, + self.configuration["access_id"], + self.configuration["secret_key"], + "GET", + ) + endpoint = self.configuration["base_url"] + api_path + + # Fetching owner_name + response = self._api_calls( + requests.get( + endpoint, + headers=add_user_agent(headers), + proxies=self.proxy, + verify=self.ssl_validation, + ) + ) + if ( + response.json()["status"] == "Success" + and "data" in response.json() + and "owner" in response.json()["data"] + and "name" in response.json()["data"]["owner"] + ): + return response.json()["data"]["owner"]["name"] + + # Owner not able to fetch. + self.logger.error( + f"Error while fetching owner information. " + f"Error: {response.json()['message']}." + ) + return None + + def get_group_names(self) -> Dict: + """Get names of available group along with id. + + Returns: + Dict: dictionary of group name as key and group id as value. + """ + owner_name = self.get_owner() + if owner_name: + query = urllib.parse.quote(f"ownerName == '{owner_name}'") + api_path = f"/api/v3/groups?tql={query}&resultLimit={LIMIT}" + url = self.configuration.get("base_url") + api_path + group_names = {} + + while True: + headers = self._get_headers_for_auth( + api_path, + self.configuration["access_id"], + self.configuration["secret_key"], + "GET", + ) + + # Fetching group Name based on owner name. + response = self._api_calls( + requests.get( + url, + headers=add_user_agent(headers), + proxies=self.proxy, + verify=self.ssl_validation, + ) + ) + if response.json()["status"] == "Success": + for group_info in response.json()["data"]: + if ( + "name" in group_info + and "id" in group_info + and group_info["name"] not in group_names + ): + group_names[group_info["name"]] = group_info["id"] + + if response.json().get("next", None): + api_path = ( + response.json() + .get("next") + .replace(self.configuration["base_url"], "") + ) + url = response.json().get("next") + else: + return group_names + else: + # Groups not able to fetch. + self.logger.error( + f"Error while fetching group details. " + f"Error: {response.json()['message']}." + ) + break + + def get_action_fields(self, action: Action) -> List[Dict]: + """Get action fields for a given action. + + Args: + action (Action): Given action. + + Returns: + List[Dict]: List of configuration parameters for a given action. + """ + if action.value == "add_to_group": + group_names = dict(sorted(self.get_group_names().items())) + group_types = [ + "Adversary", + "Attack Pattern", + "Campaign", + "Course of Action", + "Email", + "Event", + "Incident", + "Intrusion Set", + "Malware", + "Tactic", + "Task", + "Threat", + "Tool", + "Vulnerability", + ] # Document, Report, Signature not supported. + return [ + { + "label": "Add to Existing Group.", + "key": "group_name", + "type": "choice", + "choices": [ + {"key": group_name, "value": group_id} + for group_name, group_id in group_names.items() + ] + + [{"key": "Create New Group", "value": "create_group"}], + "mandatory": True, + "description": "Available groups.", + }, + { + "label": "Name of New Group (only applicable for Create " + "New Group).", + "key": "new_group_name", + "type": "text", + "mandatory": False, + "default": "", + "description": "Name of new group in which you want to " + "add all your IoCs.", + }, + { + "label": "Type of New Group (only applicable for Create " + "New Group).", + "key": "new_group_type", + "type": "choice", + "choices": [ + {"key": group_type, "value": group_type} + for group_type in group_types + ], + "mandatory": False, + "default": "Incident", + "description": "Select group type for new group.", + }, + ] + + def validate_action(self, action: Action) -> ValidationResult: + """Validate action configuration. + + Returns: + ValidationResult: Valid configuration or not for action. + """ + if action.value not in ["add_to_group"]: + return ValidationResult( + success=False, message="Invalid Action Provided." + ) + if ( + action.value in ["add_to_group"] + and action.parameters["group_name"] == "create_group" + and action.parameters["new_group_name"].strip() == "" + ): + return ValidationResult( + success=False, message="Invalid Name of New Group provided." + ) + return ValidationResult( + success=True, + message="Action configuration validated.", + ) diff --git a/threat_connect/manifest.json b/threat_connect/manifest.json new file mode 100644 index 0000000..8bb0d1e --- /dev/null +++ b/threat_connect/manifest.json @@ -0,0 +1,100 @@ +{ + "name": "ThreatConnect", + "id": "threat_connect", + "version": "1.0.0-debug", + "description": "The ThreatConnect plugin fetches indicators of type \"Malware\" and \"URL\". The data is processed and indicators are reported to Netskope CTE. The Plugin also support of sharing indicators to ThreatConnect Platform. To configure the plugin, you will need Access Id and Secret Key from ThreatConnect.", + "patch_supported": true, + "push_supported": true, + "configuration": [ + { + "label": "Base URL", + "key": "base_url", + "type": "text", + "default": "", + "mandatory": true, + "description": "ThreatConnect API Base URL." + }, + { + "label": "Access ID", + "key": "access_id", + "type": "text", + "default": "", + "mandatory": true, + "description": "ThreatConnect API Access ID." + }, + { + "label": "Secret Key", + "key": "secret_key", + "type": "password", + "default": "", + "mandatory": true, + "description": "ThreatConnect API Secret Key." + }, + { + "label": "Type of Threat IoC", + "key": "threat_type", + "type": "choice", + "choices": [ + { + "key": "Both", + "value": "Both" + }, + { + "key": "Malware", + "value": "File" + }, + { + "key": "URL", + "value": "URL" + } + ], + "mandatory": true, + "default":"Both", + "description": "Type of threat IoC you want to pull from ThreatConnect." + }, + { + "label": "Enable Tagging", + "key": "enable_tagging", + "type": "choice", + "choices": [ + { + "key": "Yes", + "value": "Yes" + }, + { + "key": "No", + "value": "No" + } + ], + "default": "No", + "mandatory": false, + "description": "Enable/Disable tagging functionality." + }, + { + "label": "Enable Polling", + "key": "is_pull_required", + "type": "choice", + "choices": [ + { + "key": "Yes", + "value": "Yes" + }, + { + "key": "No", + "value": "No" + } + ], + "default": "Yes", + "mandatory": false, + "description": "Enable/Disable polling data from ThreatConnect." + }, + { + "label": "Initial Range (in days)", + "key": "days", + "type": "number", + "mandatory": false, + "default": 7, + "description": "Number of days to pull the data for the initial run." + } + ] +} \ No newline at end of file From 4ad8975f1785b0a0b2ef5b732d374d208e025f00 Mon Sep 17 00:00:00 2001 From: Chirag Wadhwani Date: Thu, 28 Sep 2023 18:43:11 +0530 Subject: [PATCH 2/4] CTO Webhook 1.0.0 - Initial release --- webhook_cto/CHANGELOG.md | 3 + webhook_cto/__init__.py | 1 + webhook_cto/icon.png | Bin 0 -> 20797 bytes webhook_cto/main.py | 261 ++++++++++++++++++++ webhook_cto/manifest.json | 24 ++ webhook_cto/utils/webhook_constants.py | 38 +++ webhook_cto/utils/webhook_helper.py | 320 +++++++++++++++++++++++++ 7 files changed, 647 insertions(+) create mode 100644 webhook_cto/CHANGELOG.md create mode 100644 webhook_cto/__init__.py create mode 100644 webhook_cto/icon.png create mode 100644 webhook_cto/main.py create mode 100644 webhook_cto/manifest.json create mode 100644 webhook_cto/utils/webhook_constants.py create mode 100644 webhook_cto/utils/webhook_helper.py diff --git a/webhook_cto/CHANGELOG.md b/webhook_cto/CHANGELOG.md new file mode 100644 index 0000000..c1d3c0c --- /dev/null +++ b/webhook_cto/CHANGELOG.md @@ -0,0 +1,3 @@ +# 1.0.0 +## Added +- Initial release. \ No newline at end of file diff --git a/webhook_cto/__init__.py b/webhook_cto/__init__.py new file mode 100644 index 0000000..3dbb5eb --- /dev/null +++ b/webhook_cto/__init__.py @@ -0,0 +1 @@ +"""ServiceNow ITSM plugin.""" diff --git a/webhook_cto/icon.png b/webhook_cto/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..02ca1dbd90b4c02c5afba6870ef526a7d2cee51d GIT binary patch literal 20797 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 tuple: + """Get plugin name and version from manifest. + Returns: + tuple: Tuple of plugin's name and version fetched from manifest. + """ + try: + file_path = os.path.join( + str(os.path.dirname(os.path.abspath(__file__))), + "manifest.json", + ) + with open(file_path, "r") as manifest: + manifest_json = json.load(manifest) + plugin_name = manifest_json.get("name", PLATFORM_NAME) + plugin_version = manifest_json.get("version", PLUGIN_VERSION) + return (plugin_name, plugin_version) + except Exception as exp: + self.logger.info( + message=( + f"{MODULE_NAME} {PLATFORM_NAME}: Error occurred while" + " getting plugin details. Error: {}".format(exp) + ), + details=traceback.format_exc(), + ) + return (PLATFORM_NAME, PLUGIN_VERSION) + + def _add_user_agent(self, headers=None): + """Add User-Agent in the headers of any request. + + Returns: + Dict: Dictionary after adding User-Agent. + """ + headers = add_user_agent(headers) + plugin_name = self.plugin_name.lower().replace(" ", "-") + ce_added_agent = headers.get("User-Agent", "netskope-ce") + user_agent = "{}-{}-{}-v{}".format( + ce_added_agent, MODULE_NAME.lower(), plugin_name, self.plugin_version + ) + headers.update({"User-Agent": user_agent}) + return headers + + def create_task(self, alert, mappings, queue): + """Create an incident on ServiceNow.""" + + webhook_url = self.configuration.get("params", {}).get("webhook_url", "").strip() + json_object_str = mappings.get("json_object", "") + + self.logger.info( + f"{self.log_prefix} [Debug]: Mappings: {mappings}" + ) + self.logger.info( + f"{self.log_prefix} [Debug]: Json object string: {json_object_str}" + ) + self.logger.info( + f"{self.log_prefix} [Debug]: Json object string repr: {repr(json_object_str)}" + ) + if not json_object_str: + self.logger.error( + f"{self.log_prefix}: No payload found, hence nothing will be sent to the Webhook." + " A valid JSON value should be passed against 'JSON Object' field while configuring the queue." + ) + raise WebhookException("No payload found.") + + try: + json_object = json.loads(json_object_str) + self.logger.info( + f"{self.log_prefix} [Debug]: Json object: {json_object_str}" + ) + if not (json_object, dict): + self.logger.error( + f"{self.log_prefix}: Invalid payload found, hence nothing will be sent to the webhook." + " A valid JSON value should be passed against 'JSON Object' field while configuring the queue." + ) + raise WebhookException("Invalid payload found.") + except Exception as e: + self.logger.error( + f"{self.log_prefix}: Invalid payload found, hence nothing will be sent to the webhook." + f" A valid JSON value should be passed against 'JSON Object' field while configuring the queue. Error: {e}", + details=traceback.format_exc() + ) + raise WebhookException("Invalid payload found.") + + resp_json = self.webhook_helper.api_helper( + url=webhook_url, + method="POST", + is_handle_error_required=True, + logger_msg=( + "sending data to the Webhook" + ), + json=json_object + ) + + if resp_json.get("success"): + return Task(id=uuid.uuid4().hex, status=TaskStatus.NOTIFICATION) + + self.logger.error(f"{self.log_prefix}: Error occurred while sending data to the webhook.") + raise WebhookException("Error occurred while sending data to the webhook.") + + def sync_states(self, tasks: List[Task]) -> List[Task]: + """Sync all task states.""" + return tasks + + def update_task( + self, task: Task, alert: Alert, mappings: Dict, queue: Queue + ) -> Task: + """Return the task as it is.""" + return task + + def validate_url(self, url): + """Validate the url""" + + if not url: + message = "Webhook URL is a required parameter." + return False, message + parsed_url = urlparse(url) + message = "Validated Webhook URL successfully." + if not (parsed_url.scheme and parsed_url.netloc): + message = "Invalid Webhook URL provided." + return False, message + return True, message + + def validate_step(self, name, configuration): + """Validate a given step.""" + if name != "params": + return ValidationResult( + success=True, message="Validation successful." + ) + webhook_url = configuration.get("params", {}).get("webhook_url", "").strip() + result, message = self.validate_url(webhook_url) + if not result: + self.logger.error( + f"{self.log_prefix}: Validation error occurred. {message}" + ) + return ValidationResult( + success=False, + message=message + ) + return ValidationResult( + success=True, message="Validation successful." + ) + + def get_available_fields(self, configuration): + """Get list of all the available fields.""" + + fields = [ + MappingField( + label="JSON Object", value="json_object" + ) + ] + return fields + + def get_default_mappings(self, configuration): + """Get default mappings.""" + return { + "mappings": [ + FieldMapping( + extracted_field="custom_message", + destination_field="json_object", + custom_message="", + ) + ], + "dedup": [], + } + + def get_queues(self) -> List[Queue]: + """Get list of queues.""" + return [Queue(label="Notification", value="notification")] \ No newline at end of file diff --git a/webhook_cto/manifest.json b/webhook_cto/manifest.json new file mode 100644 index 0000000..1624df4 --- /dev/null +++ b/webhook_cto/manifest.json @@ -0,0 +1,24 @@ +{ + "name": "Webhook", + "description": "Webhook CTO.", + "id": "webhook_cto", + "version": "1.0.0", + "pulling_supported": false, + "receiving_supported": true, + "configuration": [ + { + "label": "Configuration Parameters", + "name": "params", + "type": "step", + "fields": [ + { + "label": "Webhook URL", + "key": "webhook_url", + "type": "text", + "mandatory": true, + "description": "Webhook URL." + } + ] + } + ] +} \ No newline at end of file diff --git a/webhook_cto/utils/webhook_constants.py b/webhook_cto/utils/webhook_constants.py new file mode 100644 index 0000000..ecd9e1d --- /dev/null +++ b/webhook_cto/utils/webhook_constants.py @@ -0,0 +1,38 @@ +""" +BSD 3-Clause License + +Copyright (c) 2021, Netskope OSS +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +CTO Webhook plugin constants. +""" +MAX_API_CALLS = 3 +DEFAULT_WAIT_TIME = 60 +PLATFORM_NAME = "Webhook" +MODULE_NAME = "CTO" +PLUGIN_VERSION = "1.0.0" \ No newline at end of file diff --git a/webhook_cto/utils/webhook_helper.py b/webhook_cto/utils/webhook_helper.py new file mode 100644 index 0000000..442e416 --- /dev/null +++ b/webhook_cto/utils/webhook_helper.py @@ -0,0 +1,320 @@ +""" +BSD 3-Clause License + +Copyright (c) 2021, Netskope OSS +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +CTO Webhook plugin helper module. +""" + +import json +import time +import traceback +from typing import Dict, Union + +import requests +from .webhook_constants import ( + MAX_API_CALLS, + DEFAULT_WAIT_TIME, + MODULE_NAME, + PLUGIN_VERSION, + PLATFORM_NAME +) +from netskope.common.utils import add_user_agent + + +class WebhookException(Exception): + """WebhookException exception class.""" + + pass + + +class WebhookPluginHelper(object): + """WebhookPluginHelper class. + + Args: + object (object): Object class. + """ + + def __init__( + self, logger, log_prefix: str, plugin_name: str, plugin_version: str + ): + """WebhookPluginHelper initializer. + + Args: + logger (logger object): Logger object. + log_prefix (str): log prefix. + plugin_name (str): Plugin name. + plugin_version (str): Plugin version. + """ + self.log_prefix = log_prefix + self.logger = logger + self.plugin_name = plugin_name + self.plugin_version = plugin_version + + def _add_user_agent(self, headers: Union[Dict, None] = None) -> Dict: + """Add User-Agent in the headers for third-party requests. + + Args: + headers (Dict): Dictionary containing headers for any request. + Returns: + Dict: Dictionary after adding User-Agent. + """ + headers = add_user_agent(headers) + ce_added_agent = headers.get("User-Agent", "netskope-ce") + user_agent = "{}-{}-{}-v{}".format( + ce_added_agent, + MODULE_NAME.lower(), + self.plugin_name.lower().replace(" ", "-"), + self.plugin_version, + ) + headers.update({"User-Agent": user_agent}) + return headers + + def api_helper( + self, + logger_msg: str, + url, + method, + params=None, + data=None, + headers=None, + verify=True, + proxies=None, + json=None, + is_handle_error_required=True, + ): + """API Helper perform API request to ThirdParty platform + and captures all the possible errors for requests. + + Args: + request (request): Requests object. + logger_msg (str): Logger string. + is_handle_error_required (bool, optional): Is handling status + code is required?. Defaults to True. + + Returns: + dict: Response dictionary. + """ + try: + for retry_counter in range(MAX_API_CALLS): + response = requests.request( + url=url, + method=method, + params=params, + data=data, + headers=self._add_user_agent(headers), + verify=verify, + proxies=proxies, + json=json, + ) + if ( + response.status_code >= 500 and response.status_code <= 600 + ): + api_err_msg = str(response.text) if response.text else "No error message" + + if retry_counter == MAX_API_CALLS - 1: + err_msg = ( + "Received exit code {}, HTTP Server Error while" + " {}. Max retries for rate limit " + "handler exceeded hence returning status" + " code {}.".format( + response.status_code, + logger_msg, + response.status_code, + ) + ) + self.logger.error( + message=f"{self.log_prefix}: {err_msg}", + details=api_err_msg, + ) + raise WebhookException(err_msg) + self.logger.error( + message=( + "{}: Received exit code {}, HTTP Server Error " + "while {}. Retrying after {} " + "seconds. {} retries remaining.".format( + self.log_prefix, + response.status_code, + logger_msg, + DEFAULT_WAIT_TIME, + MAX_API_CALLS - 1 - retry_counter, + ) + ), + details=api_err_msg, + ) + time.sleep(DEFAULT_WAIT_TIME) + else: + return ( + self.handle_error(response, logger_msg) + if is_handle_error_required + else response + ) + except requests.exceptions.ProxyError as error: + err_msg = ( + "Proxy error occurred. Verify the provided " + "proxy configuration." + ) + self.logger.error( + message=f"{self.log_prefix}: {err_msg} Error: {error}", + details=traceback.format_exc(), + ) + raise WebhookException(err_msg) + except requests.exceptions.ConnectionError as error: + err_msg = ( + "Unable to establish connection with {} " + "platform. Proxy server or {}" + " server is not reachable.".format( + self.plugin_name, self.plugin_name + ) + ) + self.logger.error( + message=f"{self.log_prefix}: {err_msg} Error: {error}", + details=traceback.format_exc(), + ) + raise WebhookException(err_msg) + except requests.HTTPError as err: + err_msg = f"HTTP Error occurred while {logger_msg}." + self.logger.error( + message=f"{self.log_prefix}: {err_msg} Error: {err}", + details=traceback.format_exc(), + ) + raise WebhookException(err_msg) + except WebhookException as exp: + raise WebhookException(exp) + except Exception as exp: + err_msg = ( + "Unexpected error occurred while requesting " + f"to {self.plugin_name}. Error: {exp}" + ) + self.logger.error( + message=f"{self.log_prefix}: {err_msg}", + details=traceback.format_exc(), + ) + raise WebhookException(err_msg) + + def parse_response(self, response: requests.models.Response): + """Parse Response will return JSON from response object. + + Args: + response (response): Response object. + + Returns: + Any: Response Json. + """ + try: + return response.json() + except json.JSONDecodeError as err: + err_msg = ( + f"Invalid JSON response received from API. Error: {str(err)}" + ) + self.logger.error( + message=f"{self.log_prefix}: {err_msg}", + details=f"API response: {response.text}", + ) + raise WebhookException(err_msg) + except Exception as exp: + err_msg = ( + "Unexpected error occurred while parsing" + f" json response. Error: {exp}" + ) + self.logger.error( + message=f"{self.log_prefix}: {err_msg}", + details=f"API Response: {response.text}", + ) + raise WebhookException(err_msg) + + def handle_error( + self, resp: requests.models.Response, logger_msg: str + ) -> Dict: + """Handle the different HTTP response code. + + Args: + resp (requests.models.Response): Response object returned + from API call. + logger_msg: logger message. + Returns: + dict: Returns the dictionary of response JSON when the + response code is 200. + Raises: + WebhookException: When the response code is + not in 200 range. + """ + if resp.status_code in [200, 201, 202, 204]: + return {"success": True} + elif resp.status_code == 403: + err_msg = "Received exit code 403, Forbidden while {}.".format( + logger_msg + ) + self.logger.error( + message=f"{self.log_prefix}: {err_msg}", + details=str(resp.text), + ) + raise WebhookException(err_msg) + elif resp.status_code == 404: + err_msg = ( + "Received exit code 404, Resource not found while {}.".format( + logger_msg + ) + ) + self.logger.error( + message=f"{self.log_prefix}: {err_msg}", + details=str(resp.text), + ) + raise WebhookException(err_msg) + elif resp.status_code >= 400 and resp.status_code < 500: + err_msg = ( + "Received exit code {}, HTTP client error while {}.".format( + resp.status_code, logger_msg + ) + ) + self.logger.error( + message=f"{self.log_prefix}: {err_msg}", + details=str(resp.text), + ) + raise WebhookException(err_msg) + elif resp.status_code >= 500 and resp.status_code < 600: + err_msg = ( + "Received exit code {}. HTTP Server Error while {}.".format( + resp.status_code, logger_msg + ) + ) + self.logger.error( + message=f"{self.log_prefix}: {err_msg}", + details=str(resp.text), + ) + raise WebhookException(err_msg) + else: + err_msg = "Received exit code {}. HTTP Error while {}.".format( + resp.status_code, logger_msg + ) + self.logger.error( + message=f"{self.log_prefix}: {err_msg}", + details=str(resp.text), + ) + raise WebhookException(err_msg) From e3bf5757d5a9bcaf8fe6270b9e8c1fa7421131eb Mon Sep 17 00:00:00 2001 From: Chirag Wadhwani Date: Thu, 28 Sep 2023 18:48:47 +0530 Subject: [PATCH 3/4] Webhook: Updated the docstring --- webhook_cto/__init__.py | 2 +- webhook_cto/main.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/webhook_cto/__init__.py b/webhook_cto/__init__.py index 3dbb5eb..cf1552e 100644 --- a/webhook_cto/__init__.py +++ b/webhook_cto/__init__.py @@ -1 +1 @@ -"""ServiceNow ITSM plugin.""" +"""Webhook CTO plugin.""" diff --git a/webhook_cto/main.py b/webhook_cto/main.py index fe4f4d2..f23c240 100644 --- a/webhook_cto/main.py +++ b/webhook_cto/main.py @@ -30,7 +30,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ -"""ServiceNow ITSM plugin.""" +"""Webhook CTO plugin.""" from typing import List, Dict @@ -75,7 +75,7 @@ def __init__( *args, **kwargs, ): - """Initialize ServiceNow plugin class.""" + """Initialize Webhook plugin class.""" super().__init__( name, *args, @@ -134,7 +134,7 @@ def _add_user_agent(self, headers=None): return headers def create_task(self, alert, mappings, queue): - """Create an incident on ServiceNow.""" + """Send notification to the webhook.""" webhook_url = self.configuration.get("params", {}).get("webhook_url", "").strip() json_object_str = mappings.get("json_object", "") From 2562550532004897001939ceb8cdc1255b465d5d Mon Sep 17 00:00:00 2001 From: Chirag Wadhwani Date: Thu, 28 Sep 2023 18:57:04 +0530 Subject: [PATCH 4/4] Webhook: Updated the icon --- webhook_cto/icon.png | Bin 20797 -> 19218 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/webhook_cto/icon.png b/webhook_cto/icon.png index 02ca1dbd90b4c02c5afba6870ef526a7d2cee51d..7b95c9efc2380f8aed38a055d7f6b13b2cc793ed 100644 GIT binary patch literal 19218 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