def __init__(self, status_code, message): self.status_code = status_code self.message = message["message"] raise PluginException( cause= "Received HTTP %d status code from GreyNoise. Verify your input and try again." % self.status_code, assistance= "If the issue persists please contact GreyNoise support.", data=f"{self.status_code}, {self.message}", )
def run(self, params={}): equation = params.get(Input.EQUATION) result = Calculate.execute_equation(equation) if result is None: raise PluginException( cause="Calculation error", assistance="Error occurred while calculating the equation. Check to make sure it is valid and try " "again. ", ) return {Output.RESULT: result}
def get_json(response): json_ = response.json() if "errors" in json_: details = '' if len(json_['errors']) > 0 and 'details' in json_['errors'][0]: details = json_['errors'][0]["detail"] raise PluginException( cause='Received an error response from AbuseIPDB.', assistance=details) return json_
def _get_kind(address: str): if validators.ipv4(address): return "IPv4Address" elif validators.ipv6(address): return "IPv6Address" elif validators.ipv4_cidr(address): return "IPv4Network" raise PluginException( cause=f"Kind not detected from address: {address}", assistance="Check address input and try again. Allowed kind are: " "IPv4Address, IPv6Address, IPv4Network.", )
def get_group_type(self, name): group = self.get_group(name) for groups in group.get("address_group", {}): if "ipv4" in groups: return "ipv4" if "ipv6" in groups: return "ipv6" raise PluginException( cause="The address group does not exist in SonicWall.", assistance="Please enter valid names and try again.", )
def search_service_request_template(self, identifier: str) -> dict: template = self._call_api( "GET", f"odata/businessobject/servicereqtemplates?$search={identifier}" ).get("value") if template and len(template) > 1: raise PluginException( cause="Multiple employees found.", assistance= f"Search for {template} returned more than 1 result. Please provide a unique identifier.", ) if template: return template[0] raise PluginException( cause="No service request templates found.", assistance= f"No service request templates found using data provided - {identifier}. Please validate and try again.", )
def run(self, params={}): decoded = base64.b64decode(params[Input.CSV]).decode() validation = params.get(Input.VALIDATION) if validation: csv_good = utils.csv_syntax_good(decoded) if not csv_good: raise PluginException(cause="Malformed CSV", assistance="Wrong CSV syntax") list_of_dicts = utils.csv_to_dict(decoded, self) return {Output.JSON: list_of_dicts}
def __find_in_whitelist(agent_obj: dict, whitelist: list): for key, value in agent_obj.items(): if key in ["externalIp", "computerName", "id", "uuid"]: if value in whitelist: raise PluginException( cause="Agent found in the whitelist.", assistance= f"If you would like to block this host, remove {value} from the whitelist and try again.", ) if key == "networkInterfaces": network_dict = value[0] for network_key, network_val in network_dict.items(): if network_key in ["inet", "inet6"]: for ip_address in network_val: if ip_address in whitelist: raise PluginException( cause="Agent found in the whitelist.", assistance= f"If you would like to block this host, remove {ip_address} from the whitelist and try again.", ) return
def search_employee(self, identifier: str) -> dict: employees = self._call_api( "GET", f"odata/businessobject/employees?$search={identifier}").get( "value") if employees and len(employees) > 1: raise PluginException( cause="Multiple employees found.", assistance= f"Search for {identifier} returned more than 1 result. " "Please provide a unique identifier.", ) if employees: return employees[0] raise PluginException( cause="No employees found.", assistance= f"No employees found using data provided - {identifier}. Please validate and try again.", )
def run(self, params={}): start, end = params.get(Input.START), params.get(Input.END) try: agents = self.connection.client.agents.list(start=start, end=end) except (ThreatStackAPIError, ThreatStackClientError, APIRateLimitError) as e: raise PluginException(cause="An error occurred!", assistance=e) # Consume the generator agents = [clean(agent) for agent in agents] return {Output.AGENTS: agents, Output.COUNT: len(agents)}
def valdate_patch_group_ids(self, patch_group_ids: list): invalid_ids = [] for patch_group_id in patch_group_ids: if not self.connection.ivanti_api.get_patch_group(patch_group_id): invalid_ids.append(patch_group_id) if len(invalid_ids) >= 1: raise PluginException( cause="Invalid Patch Group ID provided.", assistance= f"Following Patch Group IDs do not exist: {str(invalid_ids)[1:-1]}.", ) return
def run(self, params={}): files = None if params.get(Input.OBJ_TYPE) and \ params.get(Input.OBJ_TYPE) != 'file' and \ Input.FILE in params and params.get(Input.FILE).get('content'): raise PluginException( cause='Missing file content.', assistance='Complete the file input with base64 file content.') if params.get(Input.OBJ_TYPE) == 'file' and Input.OBJ_URL in params: raise PluginException( cause='Invalid input.', assistance= 'File submission from URL only possible with type "url" or "download".' ) if params.get( Input.OBJ_TYPE) != 'url' and Input.OBJ_EXT_BROWSER in params: raise PluginException( cause='Invalid input.', assistance='Browser name only possible with type "url".') if params.get(Input.OBJ_TYPE) != 'download' and ( Input.OBJ_EXT_USERAGENT in params or Input.OPT_PRIVACY_HIDESOURCE in params): raise PluginException( cause='Invalid input.', assistance='User agent only possible with type "download".') if params.get(Input.OBJ_TYPE ) == 'file' and Input.FILE in params and params.get( Input.FILE).get('content'): file = params.get(Input.FILE) files = { 'file': (file.get('filename'), base64.decodebytes(file.get('content').encode('ascii'))) } if params.get(Input.FILE): params.pop(Input.FILE) task_result = self.connection.any_run_api.run_analysis(params, files) return {Output.UUID: task_result.get("data", {}).get('taskid', None)}
def make_json_request(self, method, path, params=None, data=None, headers=None, files=None, full_response: bool = False): response = {"text": ""} try: response = requests.request(method, f"{self.url}/{path}", data=data, params=params, files=files, headers=headers, verify=self.verify_ssl) if response.status_code == 403: raise PluginException(preset=PluginException.Preset.API_KEY) if response.status_code >= 400: raise PluginException(preset=PluginException.Preset.UNKNOWN, data=response.text) if 200 <= response.status_code < 300: if full_response: return response return response.json() raise PluginException(preset=PluginException.Preset.UNKNOWN, data=response.text) except json.decoder.JSONDecodeError as e: self.logger.info(f"Invalid JSON: {e}") raise PluginException(preset=PluginException.Preset.INVALID_JSON, data=response.text) except requests.exceptions.HTTPError as e: self.logger.info(f"Call to McAfee ATD API failed: {e}") raise PluginException(preset=PluginException.Preset.UNKNOWN, data=response.text)
def run(self, params={}): bl_name = params.get(Input.NAME) bl_desc = params.get(Input.DESCRIPTION) domain_id = params.get(Input.DOMAIN_ID, "") hashes = params.get(Input.HASHES) if not hashes: raise PluginException(cause="At least one hash must be provided for the blacklist!", assistance="Ensure at least one hash is provided as step input.") # Verify hashes are of the same type self._verify_hash_input(hashes=hashes) # Get the hash type hash_type = self._is_md5_or_sha256(hashes[0]) if hash_type is HashType.sha256: raise PluginException(cause="SHA256 hashes are not supported!", assistance="Ensure only MD5 hashes are being used for input!") # If no domain_id specified, then blacklist should be global. Get all domain IDs the connection can access if not domain_id: self.logger.info("No domain IDs were specified, defaulting to global blacklisting!") domains = self.connection.api_client.get_all_accessible_domains() domains_to_blacklist = [domain["id"] for domain in domains] else: domains_to_blacklist = [domain_id] self.logger.info("Starting hash blacklisting...") try: blacklist_ids = self.connection.api_client.blacklist_files(blacklist_data=hashes, blacklist_description=bl_desc, domain_ids=domains_to_blacklist, hash_type=hash_type, name=bl_name) except APIException as e: raise PluginException(cause="An error occurred while attempting to blacklist hashes!", assistance=e.message) self.logger.info("Hash blacklisting complete!") return {Output.BLACKLIST_IDS: blacklist_ids}
def run(self, params={}): success = False hash_input = params.get(Input.HASH) if not validators.sha256(hash_input): raise PluginException( cause="An invalid hash was provided.", assistance="Please enter a SHA256 hash and try again.", ) if params.get(Input.BLACKLIST_STATE): action = self.connection.client.blacklist( hash_input, params.get(Input.DESCRIPTION)) success = action.get("id") is not None else: for page in range(1, 9999): list_of_blacklist_item = self.connection.client.get_blacklists( page) uuid = None for e in list_of_blacklist_item.get("items", []): if e.get("properties", {}).get("sha256") == hash_input: uuid = e.get("id") break if uuid is None: raise PluginException( cause= "Unable to unblacklist a hash that is not in the blacklist.", assistance= "Please provide a hash that is already blacklisted.", ) action = self.connection.client.unblacklist(uuid) success = action.get("deleted") is not None if page + 1 > list_of_blacklist_item.get("pages", {}).get("total"): break return {Output.SUCCESS: success}
def _call_api(self, method: str, endpoint: str, params: dict = None, data: dict = None, json: dict = None): _url = self.base_url + endpoint response = requests.request(url=_url, method=method, params=params, data=data, json=json, headers=self.headers) if response.status_code == 401: raise PluginException(preset=PluginException.Preset.API_KEY) if response.status_code == 403: raise PluginException(preset=PluginException.Preset.UNAUTHORIZED) if response.status_code == 404: raise PluginException( cause="No results found.\n", assistance="Please provide valid inputs or verify the endpoint/URL/hostname configured in your plugin.\n", data=f"{response.text}\nurl: {_url}", ) if 400 <= response.status_code < 500: raise PluginException(preset=PluginException.Preset.UNKNOWN, data=response.text) if response.status_code >= 500: raise PluginException(preset=PluginException.Preset.SERVER_ERROR, data=response.text) if endpoint.endswith("risklist"): return dict(xmltodict.parse(response.text)) try: return response.json() except JSONDecodeError: raise PluginException(preset=PluginException.Preset.INVALID_JSON, data=response.text)
def _call_api(self, method: str, endpoint: str, json: dict = None): response = requests.request( url=self.base_url + endpoint, method=method, json=json, auth=HTTPBasicAuth(self.username, self.password), ) if response.status_code == 401: raise PluginException( preset=PluginException.Preset.USERNAME_PASSWORD) if response.status_code == 403: raise PluginException(preset=PluginException.Preset.UNAUTHORIZED) if response.status_code == 404: raise PluginException( cause= "No results found. Invalid or unreachable endpoint provided.", assistance= "Please provide valid inputs or verify the endpoint/URL/hostname configured in your plugin" " connection is correct.", ) if 400 <= response.status_code < 500: raise PluginException(preset=PluginException.Preset.UNKNOWN, data=response.text) if response.status_code >= 500: raise PluginException(preset=PluginException.Preset.SERVER_ERROR, data=response.text) try: return response.json() except JSONDecodeError: raise PluginException(preset=PluginException.Preset.INVALID_JSON, data=response.text)
def _call_api(self, method, url, params=None, json_data=None, full_response: bool = False): response = {"text": ""} try: response = requests.request( method, url, json=json_data, params=params, auth=(self.username, self.password), verify=self.ssl_verify, ) if response.status_code == 403: raise PluginException(preset=PluginException.Preset.API_KEY) if response.status_code >= 400: response_data = response.json() raise PluginException(preset=PluginException.Preset.UNKNOWN, data=response_data.message) if 200 <= response.status_code < 300: if full_response: return response return response.json() raise PluginException(preset=PluginException.Preset.UNKNOWN, data=response.text) except json.decoder.JSONDecodeError as e: self.logger.info(f"Invalid json: {e}") raise PluginException(preset=PluginException.Preset.INVALID_JSON, data=response.text) except requests.exceptions.HTTPError as e: self.logger.info(f"Call to Palo Alto MineMeld failed: {e}") raise PluginException(preset=PluginException.Preset.UNKNOWN, data=response.text)
def _call_api(self, method, url, params=None, json_data=None, files=None): response = {"text": ""} try: response = requests.request( method, url, files=files, json=json_data, params=params, headers=self.authentication_header, ) if response.status_code == 403: raise PluginException(preset=PluginException.Preset.API_KEY) if response.status_code == 429: raise PluginException(preset=PluginException.Preset.RATE_LIMIT) if response.status_code >= 400: raise PluginException(preset=PluginException.Preset.UNKNOWN, data=response.text) if 200 <= response.status_code < 300: return response.json() raise PluginException(preset=PluginException.Preset.UNKNOWN, data=response.text) except json.decoder.JSONDecodeError as e: self.logger.info(f"Invalid json: {e}") raise PluginException(preset=PluginException.Preset.INVALID_JSON, data=response.text) except requests.exceptions.HTTPError as e: self.logger.info(f"Call to Any Run failed: {e}") raise PluginException(preset=PluginException.Preset.UNKNOWN, data=response.text)
def run(self, params={}): threat_id = params.get(Input.THREAT_ID) campaign_id = params.get(Input.CAMPAIGN_ID) include_campaign_forensics = params.get( Input.INCLUDE_CAMPAIGN_FORENSICS, False) if not threat_id: threat_id = None if not campaign_id: campaign_id = None if threat_id and campaign_id: raise PluginException( cause="Both Campaign ID and Threat ID were provided.", assistance= "Only one of the following two parameters can be used: Campaign ID or Threat ID.", ) elif not threat_id and not campaign_id: raise PluginException( cause="One of the following inputs must be provided.", assistance="Please enter either Threat ID or Campaign ID.", ) if not threat_id: include_campaign_forensics = None result = insightconnect_plugin_runtime.helper.clean( self.connection.client.get_forensics({ "threatId": threat_id, "campaignId": campaign_id, "includeCampaignForensics": include_campaign_forensics, })) return { Output.GENERATED: result.get("generated"), Output.REPORTS: result.get("reports") }
def _call_api(self, method: str, path: str, token: str = None, json_data: dict = None, params: dict = None): response = {"text": ""} headers_list = [('Accept', 'application/json')] if token: headers_list.append(('Authorization', token)) try: response = requests.request(method, self.url + path, json=json_data, params=params, headers=OrderedDict(headers_list), verify=self.verify_ssl) if response.status_code == 401: raise PluginException(preset=PluginException.Preset.USERNAME_PASSWORD) if response.status_code == 403: raise PluginException(preset=PluginException.Preset.UNAUTHORIZED) if response.status_code >= 400: response_data = response.text raise PluginException(preset=PluginException.Preset.UNKNOWN, data=response_data) if 200 <= response.status_code < 300: return response.json() raise PluginException(preset=PluginException.Preset.UNKNOWN, data=response.text) except json.decoder.JSONDecodeError as e: self.logger.info(f"Invalid JSON: {e}") raise PluginException(preset=PluginException.Preset.INVALID_JSON, data=response.text) except requests.exceptions.HTTPError as e: self.logger.info(f"Call to SonicWall Capture Client API failed: {e}") raise PluginException(preset=PluginException.Preset.UNKNOWN, data=response.text)
def run(self, params={}): """ Run action""" id_ = params[Input.ID] issue = self.connection.client.issue(id=id_) if not issue: raise PluginException( cause=f'No issue found with ID: {id_}.', assistance='Please provide a valid issue ID.') result = self.connection.client.assign_issue( issue=issue, assignee=params[Input.ASSIGNEE]) return {Output.SUCCESS: result}
def file_search(self, server_filter: dict, file_filter: dict) -> dict: if not file_filter: raise PluginException(cause="File filter shouldn't be empty.", assistance="Please check this input.") return self.send_request( "POST", "/rest/sensors/action/fileSearch", payload={ "filters": server_filter, "fileFilters": file_filter }, )
def _determine_address_type(address: str) -> str: if validators.ipv4(address): return "ipv4" if validators.ipv6(address): return "ipv6" if validators.domain(address): return "fqdn" if re.search('/', address): return "cidr" raise PluginException( cause="Unknown address type provided.", assistance=f"{address} is not one of the following: IPv4, IPv6, CIDR or domain name." )
def _verify_hash_input(self, hashes: [str]) -> None: """ Checks if multiple types of hashes have been provided as input. If multiple types are found an exception will be raised. :param hashes: Hashes input :return: None """ if len(set(filter(self._is_md5_or_sha256, hashes))) > 1: raise PluginException( cause= "Multiple types of hashes were found in the hashes input!", assistance="Only MD5 hashes are allowed as input.", )
def run(self, params={}): string = params.get(Input.STRING) encoding_val = params.get(Input.ENCODING).lower() error_handler = params.get(Input.ERROR_HANDLING) try: output = string.encode(encoding_val, error_handler) except UnicodeError: raise PluginException(cause="Encoding failed.", assistance="Could not encode given string.") output = output.decode(encoding_val, error_handler) return {Output.ENCODED: output}
def _make_request(self, method, path, json_data=None): self.login() try: response = self._call_api(method, path, json_data) self._call_api("POST", "config/pending") except PluginException as e: raise PluginException(cause=e.cause, assistance=e.assistance, data=e.data) finally: self.logout() return response
def run(self, params={}): rule_id, ruleset_id = params.get(Input.RULE_ID), params.get( Input.RULESET_ID) try: rule = clean( self.connection.client.rulesets.rules(ruleset_id=ruleset_id, rule_id=rule_id)) except (ThreatStackAPIError, ThreatStackClientError, APIRateLimitError) as e: raise PluginException(cause="An error occurred!", assistance=e) return {Output.RULE: rule}
def ip_check(self, address): try: socket.inet_aton(address) return True except socket.error: self.logger.error( "The IP address is invalid, please provide a valid IPv4 address" ) raise PluginException( cause=f"An invalid IPv4 address was provided: {address}.", assistance= "Please update the action input to include a valid IPv4 address.", )
def run(self, params={}): agent_input = params.get(Input.AGENT) try: agent = self.connection.api.get_agent(agent_input) except APIException as e: raise PluginException(cause=e.cause, assistance=e.assistance, data=e.data) # Need to rename agent due to bug in yaml typing agent["agent_info"] = agent.pop("agent") return {Output.AGENT: agent}