def add_comment_command(issue_id, comment, visibility=''): url = f'rest/api/latest/issue/{issue_id}/comment' comment = { "body": comment } if visibility: comment["visibility"] = { "type": "role", "value": visibility } result = jira_req('POST', url, json.dumps(comment)) data = result.json() md_list = [] if not isinstance(data, list): data = [data] for element in data: md_obj = { 'id': demisto.get(element, 'id'), 'key': demisto.get(element, 'updateAuthor.key'), 'comment': demisto.get(element, 'body'), 'ticket_link': demisto.get(element, 'self') } md_list.append(md_obj) human_readable = tableToMarkdown(demisto.command(), md_list, "") contents = data return_outputs(readable_output=human_readable, outputs={}, raw_response=contents)
def login(): query_path = 'www/core-service/rest/LoginService/login' headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' } params = { 'login': demisto.get(demisto.params(), 'credentials.identifier'), 'password': demisto.get(demisto.params(), 'credentials.password'), 'alt': 'json' } res = send_request(query_path, headers=headers, params=params, is_login=True) if not res.ok: demisto.debug(res.text) return_error('Failed to login, check integration parameters.') try: res_json = res.json() if 'log.loginResponse' in res_json and 'log.return' in res_json.get('log.loginResponse'): auth_token = res_json.get('log.loginResponse').get('log.return') if demisto.command() not in ['test-module', 'fetch-incidents']: # this is done to bypass setting integration context outside of the cli demisto.setIntegrationContext({'auth_token': auth_token}) return auth_token return_error('Failed to login. Have not received token after login') except ValueError: return_error('Failed to login. Please check integration parameters')
def rasterize_image(): global return_code, error_message res = demisto.getFilePath(demisto.args()['EntryID']) with open(res['path'], 'r') as f: data = f.read() b64 = base64.b64encode(data) html = '<img src="data:image/png;base64, ' + b64 + '">' return_code = 0 friendly_name = 'image.png' f = open('htmlImage.html', 'w') f.write('<html style="background:white;"><body>' + html + '</body></html>') f.close() command = ['phantomjs', '/usr/local/bin/rasterize.js', 'htmlImage.html', friendly_name] if demisto.get(demisto.args(), 'width') and demisto.get(demisto.args(), 'height'): command.append(demisto.get(demisto.args(), 'width') + '*' + demisto.get(demisto.args(), 'height')) try: error_message = subprocess.check_output(command) except Exception as e: return_code = -1 error_message = e.message if return_code == 0: file = file_result_existing_file(friendly_name) file['Type'] = entryTypes['image'] demisto.results(file) else: demisto.results({'ContentsFormat': 'text', 'Type': entryTypes['error'], 'Contents': 'PhantomJS returned - ' + error_message})
def rasterize(): global return_code, error_message return_code = 0 error_message = '' url = demisto.args()['url'] if not (url.startswith("http")): url = "http://" + url friendly_name = 'url.png' if demisto.get(demisto.args(), 'type') == 'pdf': friendly_name = 'url.pdf' proxy_flag = "" if proxy: if url.startswith("https"): proxy_flag = "--proxy=" + https_proxy else: proxy_flag = "--proxy=" + http_proxy demisto.debug('rasterize proxy settings: ' + proxy_flag) command = ['phantomjs', proxy_flag, '/usr/local/bin/rasterize.js', url, friendly_name] if demisto.get(demisto.args(), 'width') and demisto.get(demisto.args(), 'height'): command.append(demisto.get(demisto.args(), 'width') + '*' + demisto.get(demisto.args(), 'height')) try: error_message = subprocess.check_output(command) except subprocess.CalledProcessError: return_code = -1 error_message = "Can't access the URL. It might be malicious, or unreachable for one of several reasons." if return_code == 0: file = file_result_existing_file(friendly_name) file['Type'] = entryTypes['image'] demisto.results(file) else: demisto.results({'ContentsFormat': 'text', 'Type': entryTypes['error'], 'Contents': 'PhantomJS returned - ' + error_message})
def add_link_command(issue_id, title, url, summary=None, global_id=None, relationship=None): req_url = f'rest/api/latest/issue/{issue_id}/remotelink' link = { "object": { "url": url, "title": title } } if summary: link['summary'] = summary if global_id: link['globalId'] = global_id if relationship: link['relationship'] = relationship result = jira_req('POST', req_url, json.dumps(link)) data = result.json() md_list = [] if not isinstance(data, list): data = [data] for element in data: md_obj = { 'id': demisto.get(element, 'id'), 'key': demisto.get(element, 'updateAuthor.key'), 'comment': demisto.get(element, 'body'), 'ticket_link': demisto.get(element, 'self') } md_list.append(md_obj) human_readable = tableToMarkdown(demisto.command(), md_list, "", removeNull=True) contents = data return_outputs(readable_output=human_readable, outputs={}, raw_response=contents)
def get_security_events_command(): ids = demisto.args().get('ids') last_date_range = demisto.args().get('lastDateRange') ids = argToList(str(ids) if isinstance(ids, int) else ids) raw_events = get_security_events(ids, last_date_range) if raw_events: events = [] contents = decode_arcsight_output(raw_events) for raw_event in contents: event = { 'Event ID': raw_event.get('eventId'), 'Time': timestamp_to_datestring(raw_event.get('endTime'), '%Y-%m-%d, %H:%M:%S'), 'Source Address': decode_ip(demisto.get(raw_event, 'source.address')), 'Destination Address': decode_ip(demisto.get(raw_event, 'destination.address')), 'Name': raw_event.get('name'), 'Source Port': demisto.get(raw_event, 'source.port'), 'Base Event IDs': raw_event.get('baseEventIds') } events.append(event) human_readable = tableToMarkdown('Security Event: {}'.format(','.join(map(str, ids))), events, removeNull=True) outputs = {'ArcSightESM.SecurityEvents(val.eventId===obj.eventId)': contents} return_outputs(readable_output=human_readable, outputs=outputs, raw_response=contents) else: demisto.results('No events were found')
def get_entries_command(): resource_id = demisto.args().get('resourceId') entry_filter = demisto.args().get('entryFilter') query_path = 'www/manager-service/services/ActiveListService/' body = REQ_SOAP_BODY(function='getEntries', auth_token=AUTH_TOKEN, resource_id=resource_id, entryList=None) res = send_request(query_path, body=body) if not res.ok: demisto.debug(res.text) return_error("Failed to get entries:\nResource ID: {}\nStatus Code: {}\nRequest Body: {}\nResponse: {}".format( resource_id, res.status_code, body, res.text)) res_json = json.loads(xml2json(res.text)) raw_entries = demisto.get(res_json, 'Envelope.Body.getEntriesResponse.return') # retrieve columns cols = demisto.get(raw_entries, 'columns') if cols: hr_columns = tableToMarkdown(name='', headers=['Columns'], t=cols, removeNull=True) if cols else 'Active list has no columns' contents = cols return_outputs(readable_output=hr_columns, outputs={}, raw_response=contents) if 'entryList' in raw_entries: entry_list = raw_entries['entryList'] if isinstance(raw_entries['entryList'], list) else [ raw_entries['entryList']] entry_list = [d['entry'] for d in entry_list if 'entry' in d] keys = raw_entries.get('columns') entries = [dict(zip(keys, values)) for values in entry_list] # if the user wants only entries that contain certain 'field:value' sets (filters) # e.g., "name:myName,eventId:0,:ValueInUnknownField" # if the key is empty, search in every key filtered = entries if entry_filter: for f in entry_filter.split(','): k, v = f.split(':') filtered = [entry for entry in filtered if ((entry.get(k) == v) if k else (v in entry.values()))] contents = decode_arcsight_output(filtered) ActiveListContext = { 'ResourceID': resource_id, 'Entries': contents, } outputs = { 'ArcSightESM.ActiveList.{id}'.format(id=resource_id): contents, 'ArcSightESM.ActiveList(val.ResourceID===obj.ResourceID)': ActiveListContext } human_readable = tableToMarkdown(name='Active List entries: {}'.format(resource_id), t=filtered, removeNull=True) return_outputs(readable_output=human_readable, outputs=outputs, raw_response=contents) else: demisto.results('Active List has no entries')
def create_incident(alert): """ Turns an alert from Canary Tools to the incident structure in Demisto :return: Demisto incident, e.g., CanaryToken triggered """ incident = { 'name': demisto.get(alert, 'description.description'), 'occurred': timestamp_to_datestring(1000 * (int(demisto.get(alert, 'description.created')))), 'rawJSON': json.dumps(alert) } return incident
def generate_md_upload_issue(data, issue_id): upload_md = [] if not isinstance(data, list): data = [data] for element in data: md_obj = { 'id': demisto.get(element, 'id'), 'issueId': issue_id, 'attachment_name': demisto.get(element, 'filename'), 'attachment_link': demisto.get(element, 'self') } upload_md.append(md_obj) return upload_md
def http_request(method, url_suffix, params=None, body=None, do_not_refresh_token=False): """ Generic request to Microsoft Graph """ token = get_token() response = requests.request( method, BASE_URL + url_suffix, headers={ 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json', 'Accept': 'application/json' }, params=params, data=body, verify=USE_SSL, ) try: data = response.json() if response.text else {} if not response.ok: if demisto.get(data, "error.message") == 'InvalidAuthenticationToken' and not do_not_refresh_token: get_token(refresh_token=True) # try refreshing the token only once (avoid endless loop) return http_request(method, url_suffix, params, body, do_not_refresh_token=True) return_error(f'API call to MS Graph failed [{response.status_code}] - {demisto.get(data, "error.message")}') elif response.status_code == 206: # 206 indicates Partial Content, reason will be in the warning header demisto.debug(str(response.headers)) return data except TypeError as ex: demisto.debug(str(ex)) return_error(f'Error in API call to Microsoft Graph, could not parse result [{response.status_code}]')
def issue_query_command(query, start_at='', max_results=None, headers=''): j_res = run_query(query, start_at, max_results) issues = demisto.get(j_res, 'issues') md_and_context = generate_md_context_get_issue(issues) human_readable = tableToMarkdown(demisto.command(), md_and_context['md'], argToList(headers)) contents = j_res outputs = {'Ticket(val.Id == obj.Id)': md_and_context['context']} return_outputs(readable_output=human_readable, outputs=outputs, raw_response=contents)
def get_token_command(): """ Fetch a Canary Token from the Canary Tools server :return: Canary Token information or file """ token = demisto.args().get('token') params = { 'canarytoken': token } res = http_request('GET', SERVER + 'canarytoken/fetch', params=params) context = demisto.get(res, 'token.canarytoken') contents = res human_readable = 'File Fetched Successfully' outputs = {'CanaryTools.Token(val.CanaryToken && val.CanaryToken === obj.CanaryToken)': context} if demisto.get(res, 'token.doc'): name = demisto.get(res, 'token.doc_name') content = demisto.get(res, 'token.doc') token_file = fileResult(name, content) demisto.results(token_file) if demisto.get(res, 'token.web_image'): name = demisto.get(res, 'token.web_image_name') content = demisto.get(res, 'token.web_image') token_file = fileResult(name, content) demisto.results(token_file) else: human_readable = tableToMarkdown('Canary Tools Tokens', res.get('token')) return_outputs(readable_output=human_readable, outputs=outputs, raw_response=contents)
def analyse_url(): args = demisto.args() url = args.get('url') internet_access = bool(strtobool(args.get('internet-access', 'true'))) comments = args.get('comments') systems = args.get('systems') should_wait = bool(strtobool(demisto.get(args, 'should_wait'))) return analyse_url_request(url, should_wait, internet_access, comments, systems)
def rasterize_email_request(html, friendly_name): global return_code, error_message f = open('htmlBody.html', 'w') f.write('<html style="background:white";>' + html + '</html>') f.close() proxy_flag = "" if proxy: proxy_flag = "--proxy=" + http_proxy demisto.debug('rasterize proxy settings: ' + proxy_flag) command = ['phantomjs', proxy_flag, '/usr/local/bin/rasterize.js', 'htmlBody.html', friendly_name] if demisto.get(demisto.args(), 'width') and demisto.get(demisto.args(), 'height'): command.append(demisto.get(demisto.args(), 'width') + '*' + demisto.get(demisto.args(), 'height')) try: error_message = subprocess.check_output(command) except Exception as e: return_code = -1 error_message = e.message
def rasterize_email_command(): html = demisto.args()['htmlBody'] friendly_name = 'email.png' if demisto.get(demisto.args(), 'type') == 'pdf': friendly_name = 'email.pdf' rasterize_email_request(html, friendly_name) if return_code == 0: file = file_result_existing_file(friendly_name) file['Type'] = entryTypes['image'] demisto.results(file) else: demisto.results({'ContentsFormat': 'text', 'Type': entryTypes['error'], 'Contents': 'PhantomJS returned - ' + error_message})
def generate_md_context_create_issue(data, project_name=None, project_key=None): create_issue_obj = {"md": [], "context": {"Ticket": []}} if project_name: data["projectName"] = project_name if project_key: data["projectKey"] = project_key elif demisto.getParam('projectKey'): data["projectKey"] = demisto.getParam('projectKey') create_issue_obj['md'].append(data) # type: ignore create_issue_obj['context']['Ticket'].append({"Id": demisto.get(data, 'id'), "Key": demisto.get(data, 'key')}) # type: ignore return create_issue_obj
def add_comment_command(issue_id, comment, visibility=''): url = f'rest/api/latest/issue/{issue_id}/comment' comment = {"body": comment} if visibility: comment["visibility"] = {"type": "role", "value": visibility} result = jira_req('POST', url, json.dumps(comment)) data = result.json() md_list = [] if not isinstance(data, list): data = [data] for element in data: md_obj = { 'id': demisto.get(element, 'id'), 'key': demisto.get(element, 'updateAuthor.key'), 'comment': demisto.get(element, 'body'), 'ticket_link': demisto.get(element, 'self') } md_list.append(md_obj) human_readable = tableToMarkdown(demisto.command(), md_list, "") contents = data return_outputs(readable_output=human_readable, outputs={}, raw_response=contents)
def get_with_limit(obj, path, limit=None): """ Get from path with optional limit """ res = demisto.get(obj, path) try: if limit: if len(res) > limit: if isinstance(res, dict): return {k: res[k] for k in res.keys()[:limit]} elif isinstance(res, list): return res[:limit] # If res has no len, or if not a list or a dictionary return res finally: return res
def signal_to_readable(signal_json): """ Convert signal response JSON to nicely formatted object """ signal = to_readable(signal_json, [ 'id', 'name', 'category', 'description', 'score', 'source_type', 'timestamp' ], {}) threat_indicators = demisto.get(signal_json, 'threat_indicators') if threat_indicators and isinstance(threat_indicators, dict): signal['ThreatIndicators'] = [{ 'IndicatorType': ti.get('indicator_type'), 'Value': ti.get('value') } for ti in threat_indicators] return signal
def create_file_ec_from_analysis_json(analysis_json): """ Creates a file entry array from the analysis json """ malware_descs = demisto.get(analysis_json, 'metadata.malware_desc') res = [] for desc in malware_descs: res.append({ 'FileName': demisto.get(desc, 'filename'), 'Size': demisto.get(desc, 'size'), 'MD5': demisto.get(desc, 'md5'), 'SHA1': demisto.get(desc, 'sha1'), 'SHA256': demisto.get(desc, 'sha256'), 'MagicType': demisto.get(desc, 'magic'), 'Type': demisto.get(desc, 'type'), }) return res
def handle_artifact_from_analysis_json(ec, hr, analysis_json, limit): ''' Populates ec and hr with artifact data from analysis json ''' hr['Artifact'] = {} for artifact in get_with_limit(analysis_json, 'artifacts', limit).values(): id = None yaras = demisto.get(artifact, 'antivirus.yara') if yaras: for yara in filter(lambda yara: 'id' in yara, yaras): id = yara['id'] break if id: artifact_key = 'ThreatGrid.Artifact(val.ID === obj.{0})'.format( id) artifact_hr_key = 'Artifact(ID = {0})'.format(id) tags = set() for yara in filter(lambda yara: 'tags' in yara, yaras): if yara['tags']: for tag in yara['tags']: tags.add(tag) # converting to list for tableToMarkdown tags = list(tags) hr['Artifact'][artifact_hr_key] = ec[artifact_key] = { 'ID': id, 'Tags': tags, 'FamilyName': demisto.get( artifact, 'antivirus.reversing_labs.classification.family_name'), 'ThreatName': demisto.get(artifact, 'antivirus.reversing_labs.threat_name') }
def search_ips(): """ Search ips with the given filters """ r = req('GET', SUB_API + 'search/ips', params=apply_search_filters()) ips = [] for ip in demisto.get(r.json(), 'data.items'): ips.append({ 'Result': demisto.get(ip, 'result'), 'Details': demisto.get(ip, 'details') }) demisto.results({ 'Type': entryTypes['note'], 'EntryContext': { 'ThreatGrid.IPs': ips }, 'HumanReadable': tableToMarkdown('ThreatGrid - IP Search', ips, ['Result', 'Details']), 'ContentsFormat': formats['json'], 'Contents': r.json() })
def search_detections_command(): """ Searches for a detection :return: EntryObject of search detections command """ d_args = demisto.args() detections_ids = argToList(d_args.get('ids')) if not detections_ids: filter_arg = d_args.get('filter') if not filter_arg: return_error( 'Command Error: Please provide at least one argument.') detections_ids = get_detections(filter_arg=filter_arg).get('resources') raw_res = get_detections_entities(detections_ids) entries = [] headers = [ 'ID', 'Status', 'System', 'ProcessStartTime', 'CustomerID', 'MaxSeverity' ] if "resources" in raw_res: for detection in demisto.get(raw_res, "resources"): detection_entry = {} for path, new_key in DETECTIONS_BASE_KEY_MAP.items(): detection_entry[new_key] = demisto.get(detection, path) behaviors = [] for behavior in demisto.get(detection, 'behaviors'): behaviors.append(behavior_to_entry_context(behavior)) detection_entry['Behavior'] = behaviors entries.append(detection_entry) hr = tableToMarkdown('Detections Found:', entries, headers=headers, removeNull=True, headerTransform=pascalToSpace) ec = {'CrowdStrike.Detection(val.ID === obj.ID)': entries} return create_entry_object(contents=raw_res, ec=ec, hr=hr)
def http_request(method, url_suffix, params=None, body=None, do_not_refresh_token=False): """ Generic request to Microsoft Graph """ token = get_token() response = requests.request( method, BASE_URL + url_suffix, headers={ 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json', 'Accept': 'application/json' }, params=params, data=body, verify=USE_SSL, ) try: data = response.json() if response.text else {} if not response.ok: if demisto.get( data, "error.message" ) == 'InvalidAuthenticationToken' and not do_not_refresh_token: get_token( refresh_token=True ) # try refreshing the token only once (avoid endless loop) return http_request(method, url_suffix, params, body, do_not_refresh_token=True) return_error( f'API call to MS Graph failed [{response.status_code}] - {demisto.get(data, "error.message")}' ) elif response.status_code == 206: # 206 indicates Partial Content, reason will be in the warning header demisto.debug(str(response.headers)) return data except TypeError as ex: demisto.debug(str(ex)) return_error( f'Error in API call to Microsoft Graph, could not parse result [{response.status_code}]' )
def get_campaign_incidents(): """ Get the campaign incidents form the incident's context :rtype: ``list`` :return: list of campaign incidents """ incident_id = demisto.incidents()[0]['id'] res = demisto.executeCommand('getContext', {'id': incident_id}) if isError(res): return_error( f'Error occurred while trying to get the incident context: {get_error(res)}' ) return demisto.get(res[0], 'Contents.context.EmailCampaign.incidents')
def get_tags(indicator): """ Return list of the indicator's tags threat_type and maltype """ tags = [] for key in ['meta.maltype', 'threat_type']: val = demisto.get(indicator, key) if val: tags.append(val) indicator_tags = indicator.get('tags', []) if indicator_tags: tags.extend([str(tag.get('name', '')) for tag in indicator_tags]) return tags
def translate_severity(issue): """ Translate issue severity to demisto Might take risk grade into account in the future """ severity = demisto.get(issue, 'severity') if severity == 'CRITICAL': return 4 if severity == 'HIGH': return 3 if severity == 'MEDIUM': return 2 if severity == 'LOW': return 1 if severity == 'INFORMATIONAL': return 0
def extract_and_validate_http_response(resp, operation_err_message, test=False): try: resp.raise_for_status() return resp.json() if not test else resp.content except requests.exceptions.HTTPError: try: err_message = resp.json().get('message') except Exception: try: err_obj = json.loads(xml2json(resp.text)) err_message = demisto.get(err_obj, 'Error.Message') except Exception: err_message = f'Could not parse error' return_error(f'{operation_err_message}: \n{err_message}')
def entity_to_readable(entity_json): """ Convert entity response JSON to nicely formatted object """ entity = to_readable( entity_json, [ 'id', 'name', 'source', 'hostname', 'risk_score', 'is_whitelisted', 'groups', 'asset_type', 'firstSeen', 'lastSeen' ], { 'asset_type': 'EntityType', 'firstSeen': 'FirstSeen', 'lastSeen': 'LastSeen' }) entity['PrimaryEntityType'] = demisto.get( entity_json, 'current_entity.primary_asset_type') return entity
def extract_transformed_dict_with_split(old_dict, transformation_dict_arr): """ Extracts new values out of old_dict using a json structure of: {'Path': 'Path to item', 'NewKey': 'Value of output key', 'Delim': 'Delimiter char', 'Index': Split Array Index} """ new_dict = {} for trans_dict in transformation_dict_arr: try: val = demisto.get(old_dict, trans_dict['Path']) if 'split' in dir(val): i = trans_dict['Index'] new_dict[trans_dict['NewKey']] = val.split( trans_dict['Delim'])[i] except Exception as ex: LOG('Error {exception} with: {tdict}'.format(exception=ex, tdict=trans_dict)) return new_dict
def main(): try: incidents = get_campaign_incidents_from_context() fields_to_display = demisto.get(demisto.context(), 'EmailCampaign.fieldsToDisplay') if incidents: update_incident_with_required_keys(incidents, KEYS_FETCHED_BY_QUERY) update_empty_fields() readable_output = get_incidents_info_md(incidents, fields_to_display) else: readable_output = NO_CAMPAIGN_INCIDENTS_MSG return_results(CommandResults(readable_output=readable_output)) except Exception as err: return_error(str(err))
def get_action_status(action_id): fullurl = BASE_URL + '/api/action/' + action_id + '/status' res = requests.get(fullurl, auth=(USERNAME, PASSWORD), verify=VERIFY_CERTIFICATE) if res.status_code < 200 or res.status_code >= 300: return_error( 'Failed to get action {} status.\nRequest URL: {}\nStatusCode: {}\nResponse Body: {}' .format(action_id, fullurl, res.status_code, res.content)) raw_action = json.loads(xml2json(res.content)) if not raw_action or 'BESAPI' not in raw_action: return None raw_action = demisto.get(raw_action, 'BESAPI.ActionResults') return raw_action.get('Status')
def main() -> None: params = demisto.params() api_key = demisto.get(params, 'credentials.password') base_api = params.get('base_url') reliability = params.get('integrationReliability') verify_certificate = not params.get('insecure', False) proxy = params.get('proxy', False) demisto.debug(f'Command being called is {demisto.command()}') try: suspicious_domain_threshold = arg_to_number( params.get('suspicious_domain_threshold', None), required=False, arg_name='suspicious_domain_threshold') top_domain_threshold = arg_to_number( params.get('top_domain_threshold'), required=True, arg_name='top_domain_threshold') if (suspicious_domain_threshold and suspicious_domain_threshold < 0)\ or top_domain_threshold < 0: # type: ignore raise DemistoException( f'AlexaV2 error: All threshold values should be greater than 0.' f'Suspicious domain threshold is {suspicious_domain_threshold}. ' f'Top domain threshold is {top_domain_threshold}.') client = Client( base_url=base_api, verify=verify_certificate, proxy=proxy, api_key=api_key, suspicious_domain_threshold= suspicious_domain_threshold, # type: ignore top_domain_threshold=top_domain_threshold, # type: ignore reliability=reliability) if demisto.command() == 'test-module': return_results(test_module(client)) elif demisto.command() == 'domain': domains = demisto.args().get('domain') return_results(alexa_domain(client, argToList(domains))) else: raise NotImplementedError( f'Command {demisto.command()} is not implemented.') except Exception as e: demisto.error(traceback.format_exc()) # print the traceback return_error( f'Failed to execute {demisto.command()} command.\nError:\n{str(e)}' )
def isDemistoAPIIntegrationAvailable(): brandName = "Demisto REST API" allInstances = demisto.getModules() brandInstances = [ instanceName for instanceName in allInstances if allInstances[instanceName]['brand'].lower() == brandName.lower() and demisto.get(allInstances[instanceName], 'state') and allInstances[instanceName]['state'] == 'active' ] if len(brandInstances) == 1: return 1 elif len(brandInstances) > 1: return 2 else: return 0
def get_update_incident_request_data(client: Client, args: Dict[str, str]): # Get Etag and other mandatory properties (title, severity, status) for update_incident command _, _, fetched_incident_data = get_incident_by_id_command(client, args) title = args.get('title') description = args.get('description') severity = args.get('severity') status = args.get('status') classification = args.get('classification') classification_reason = args.get('classification_reason') assignee_email = args.get('assignee_email') labels = argToList(args.get('labels', '')) if not title: title = demisto.get(fetched_incident_data, 'properties.title') if not description: description = demisto.get(fetched_incident_data, 'properties.description') if not severity: severity = demisto.get(fetched_incident_data, 'properties.severity') if not status: status = demisto.get(fetched_incident_data, 'properties.status') if not assignee_email: assignee_email = demisto.get(fetched_incident_data, 'properties.owner.email') existing_labels = demisto.get(fetched_incident_data, 'properties.labels') if not labels: # not provided as arg labels_formatted = existing_labels else: labels_formatted = [{ "labelName": label, "labelType": "User" } for label in argToList(labels) if label] # labels can not be blank incident_data = { 'etag': fetched_incident_data.get('etag'), 'properties': { 'title': title, 'description': description, 'severity': severity, 'status': status, 'classification': classification, 'classificationReason': classification_reason, 'labels': labels_formatted, 'owner': { 'email': assignee_email } } } remove_nulls_from_dictionary(incident_data['properties']) return incident_data
def get_email_context(email_data, mailbox): context_headers = email_data.get('payload', {}).get('headers', []) context_headers = [{'Name': v['name'], 'Value':v['value']} for v in context_headers] headers = dict([(h['Name'].lower(), h['Value']) for h in context_headers]) body = demisto.get(email_data, 'payload.body.data') body = body.encode('ascii') if body is not None else '' parsed_body = base64.urlsafe_b64decode(body) context = { 'Type': 'Gmail', 'Mailbox': ADMIN_EMAIL if mailbox == 'me' else mailbox, 'ID': email_data['id'], 'ThreadId': email_data['threadId'], 'Labels': ', '.join(email_data['labelIds']), 'Headers': context_headers, 'Attachments': email_data.get('payload', {}).get('filename', ''), # only for format 'raw' 'RawData': email_data.get('raw'), # only for format 'full' and 'metadata' 'Format': headers.get('content-type', '').split(';')[0], 'Subject': headers.get('subject'), 'From': headers.get('from'), 'To': headers.get('to'), # only for format 'full' 'Body': unicode(parsed_body, 'utf-8'), # only for incident 'Cc': headers.get('cc', []), 'Bcc': headers.get('bcc', []), 'Date': headers.get('date', ''), 'Html': None, } if 'text/html' in context['Format']: context['Html'] = context['Body'] context['Body'] = html_to_text(context['Body']) if 'multipart' in context['Format']: context['Body'], context['Html'], context['Attachments'] = parse_mail_parts( email_data.get('payload', {}).get('parts', [])) context['Attachment Names'] = ', '.join( [attachment['Name'] for attachment in context['Attachments']]) return context, headers
def get_email_context(email_data, mailbox): context_headers = email_data.get('payload', {}).get('headers', []) context_headers = [{'Name': v['name'], 'Value':v['value']} for v in context_headers] headers = dict([(h['Name'].lower(), h['Value']) for h in context_headers]) body = demisto.get(email_data, 'payload.body.data') body = body.encode('ascii') if body is not None else '' parsed_body = base64.urlsafe_b64decode(body) context = { 'Type': 'Gmail', 'Mailbox': ADMIN_EMAIL if mailbox == 'me' else mailbox, 'ID': email_data['id'], 'ThreadId': email_data['threadId'], 'Labels': ', '.join(email_data['labelIds']), 'Headers': context_headers, 'Attachments': email_data.get('payload', {}).get('filename', ''), # only for format 'raw' 'RawData': email_data.get('raw'), # only for format 'full' and 'metadata' 'Format': headers.get('content-type', '').split(';')[0], 'Subject': headers.get('subject'), 'From': headers.get('from'), 'To': headers.get('to'), # only for format 'full' 'Body': unicode(parsed_body, 'utf-8'), # only for incident 'Cc': headers.get('cc', []), 'Bcc': headers.get('bcc', []), 'Date': headers.get('date', ''), 'Html': None, } if 'text/html' in context['Format']: # type: ignore context['Html'] = context['Body'] context['Body'] = html_to_text(context['Body']) if 'multipart' in context['Format']: # type: ignore context['Body'], context['Html'], context['Attachments'] = parse_mail_parts( email_data.get('payload', {}).get('parts', [])) context['Attachment Names'] = ', '.join( [attachment['Name'] for attachment in context['Attachments']]) # type: ignore return context, headers
def main(): try: args = demisto.args() # the entry_id argument can be a list of entry ids or a single entry id entry_ids = args.get('entry_id', demisto.get(demisto.context(), 'lastCompletedTaskEntries')) entry_ids = argToList(entry_ids) entries = [demisto.executeCommand('getEntry', {'id': entry_id}) for entry_id in entry_ids] error_messages = get_errors(entries) return_results(CommandResults( readable_output='\n'.join(error_messages), outputs_prefix='ErrorEntries', outputs=error_messages, raw_response=error_messages, )) except Exception as e: return_error(f'Failed to fetch errors for the given entry id(s). Problem: {str(e)}')
def create_relationships(client: Client, indicator, ioc_type, relation_mapper): relationships: List[EntityRelationship] = [] if not client.should_create_relationships: return relationships for relation in relation_mapper: entity_b = demisto.get(indicator, relation['raw_field']) if entity_b: relationships.append( EntityRelationship(entity_a=indicator['value'], entity_a_type=ioc_type, name=relation['name'], entity_b=entity_b, entity_b_type=relation['entity_b_type'], source_reliability=client.reliability, brand=THREAT_STREAM)) return relationships
def get_samples(): """ Get samples matching the provided filters. """ r = req('GET', SUB_API + 'samples', params=handle_filters()) samples = [] for k in demisto.get(r.json(), 'data.items'): samples.append(sample_to_readable(k)) md = tableToMarkdown('ThreatGrid - List of Samples', samples, [ 'ID', 'Filename', 'State', 'Status', 'MD5', 'SHA1', 'SHA256', 'OS', 'SubmittedAt', 'StartedAt', 'CompletedAt' ]) demisto.results({ 'Type': entryTypes['note'], 'EntryContext': {'ThreatGrid.Sample(val.ID == obj.ID)': samples}, 'HumanReadable': md, 'ContentsFormat': formats['json'], 'Contents': r.json() })
def main(): fields_mapping = {} for xdr_field in XDR_INCIDENT_FIELDS: if xdr_field in demisto.args() and demisto.args().get( xdr_field) is not None: custom_field_in_demisto = demisto.args().get(xdr_field) fields_mapping[xdr_field] = custom_field_in_demisto playbook_to_run = demisto.args().get('playbook_to_run') incident_id = demisto.args().get('incident_id') first_run = demisto.args().get('first') == 'true' interval = int(demisto.args().get('interval')) xdr_incident_from_previous_run = demisto.args().get( 'xdr_incident_from_previous_run') xdr_alerts_field = demisto.args().get('xdr_alerts') xdr_file_artifacts_field = demisto.args().get('xdr_file_artifacts') xdr_network_artifacts_field = demisto.args().get('xdr_network_artifacts') verbose = demisto.args().get('verbose') == 'true' xdr_incident_markdown_field = demisto.args().get( 'xdr_incident_markdown_field') # deprecated if xdr_incident_markdown_field: # deprecated field raise ValueError( 'Deprecated xdr_incident_markdown_field argument, instead use xdr_alerts, xdr_file_artifacts, ' 'xdr_network_artifacts. For more information follow Demisto documentation.' ) previous_scheduled_task_id = demisto.get(demisto.context(), 'XDRSyncScriptTaskID') if first_run and previous_scheduled_task_id: if verbose: demisto.debug( 'Stopping previous scheduled task with ID: {}'.format( previous_scheduled_task_id)) demisto.executeCommand('StopScheduledTask', {'taskID': previous_scheduled_task_id}) demisto.setContext('XDRSyncScriptTaskID', '') xdr_incident_sync(incident_id, fields_mapping, playbook_to_run, xdr_incident_from_previous_run, first_run, interval, xdr_alerts_field, xdr_file_artifacts_field, xdr_network_artifacts_field, verbose)
def create_rpz_rule(self, rule_type: Optional[str], object_type: Optional[str], name: Optional[str], rp_zone: Optional[str], view: Optional[str], substitute_name: Optional[str], comment: Optional[str] = None) -> Dict: """Creates new response policy zone rule. Args: rule_type: Type of rule to create. object_type: Type of object to assign the rule on. name: Rule name. rp_zone: The zone to assign the rule. view: The DNS view in which the records are located. By default, the 'default' DNS view is searched. substitute_name: The substitute name to assign (In case of substitute domain only) comment: A comment for this rule. Returns: Response JSON """ canonical: Optional[str] = '' if rule_type == 'Passthru': canonical = 'rpz-passthru' if object_type == 'Client IP address' else name elif rule_type == 'Block (No data)': canonical = '*' elif rule_type == 'Substitute (domain name)': canonical = substitute_name data = assign_params(name=name, rp_zone=rp_zone, view=view, comment=comment) # if rule_type is 'Block (No such domain)', then 'canonical' is '' (empty string) but API still requires 'canonical' data.update({'canonical': canonical}) request_params = REQUEST_PARAM_CREATE_RULE suffix = demisto.get( RPZ_RULES_DICT, f'{rule_type}.{object_type}.infoblox_object_type') rule = self._http_request('POST', suffix, data=json.dumps(data), params=request_params) rule['result']['type'] = suffix return rule
def _handle_existing_outputs(anchor_hash: str, output_key: str, new_hashes_outputs: list): context = demisto.get(demisto.context(), f'{output_key}') if not context: context = [] elif not isinstance(context, list): context = [context] context = list(filter(lambda item: item.get('SourceHash') == anchor_hash, context)) if context: context = context[0].get('compared_hashes') new_hashes = [current_hash.get('hash') for current_hash in new_hashes_outputs] res = [] for item in context: if item.get('hash') not in new_hashes: res.append(item) res += new_hashes_outputs return res
def search_query(self, body: Dict[str, Any]) -> Dict[str, Any]: """ Creates a request to MISP to get all attributes filtered by query in the body argument Args: body: Dictionary containing query to filter MISP attributes. Returns: bytes representing the response from MISP API """ headers = { 'Authorization': demisto.get(demisto.params(), 'credentials.password'), "Accept": "application/json", 'Content-Type': 'application/json' } response = self._http_request('POST', full_url=f'{self._base_url}attributes/restSearch', resp_type='json', headers=headers, data=json.dumps(body), timeout=self.timeout) return response
def get_issue(issue_id, headers=None, expand_links=False, is_update=False, get_attachments=False): result = jira_req('GET', 'rest/api/latest/issue/' + issue_id) j_res = result.json() if expand_links == "true": expand_urls(j_res) attachments = demisto.get(j_res, 'fields.attachment') # list of all attachments if get_attachments == 'true' and attachments: attachments_zip = jira_req(method='GET', resource_url=f'secure/attachmentzip/{issue_id}.zip').content demisto.results(fileResult(filename=f'{j_res.get("id")}_attachments.zip', data=attachments_zip)) md_and_context = generate_md_context_get_issue(j_res) human_readable = tableToMarkdown(demisto.command(), md_and_context['md'], argToList(headers)) if is_update: human_readable += f'Issue #{issue_id} was updated successfully' contents = j_res outputs = {'Ticket(val.Id == obj.Id)': md_and_context['context']} return_outputs(readable_output=human_readable, outputs=outputs, raw_response=contents)
def extract_data_from_analysis_json(analysis_json, sample_id, limit): """ Extracts relevant data from an analysis json """ ec = {} hr = {} sample_key = 'ThreatGrid.Sample(val.ID === obj.ID)' sample_process = extract_sample_process_from_analysis_processes( demisto.get(analysis_json, 'dynamic.processes')) or {} ec[sample_key] = create_sample_ec_from_analysis_json( analysis_json, sample_id, sample_process, limit) hr['Sample'] = create_sample_hr_from_analysis_json(ec[sample_key], analysis_json, sample_process, limit) hr['File'] = ec[sample_key]["File"] = create_file_ec_from_analysis_json( analysis_json) handle_artifact_from_analysis_json(ec, hr, analysis_json, limit) hr_str = create_analysis_json_human_readable(hr) return ec, hr_str
def get_duration_html(): try: incident_id = demisto.incident().get('id', {}) context = demisto.executeCommand("getContext", {'id': incident_id}) first_date = demisto.get(context[0]['Contents']['context'], "EmailCampaign.firstIncidentDate") if not first_date: raise FieldNotFound() if isinstance(first_date, list): first_date = first_date[-1] now = datetime.now().replace(tzinfo=utc) parsed_first_date: datetime = dateutil.parser.isoparse( first_date).replace(tzinfo=utc) diff = now - parsed_first_date return f""" <table> <tr> <th style="font-size: 25px;">🕙</th> <th style="font-size: 30px;">{diff.days}</th> <th style="font-size: 30px;">:</th> <th style="font-size: 30px;">{(diff.seconds // 3600) % 24}</th> <th style="font-size: 30px;">:</th> <th style="font-size: 30px;">{(diff.seconds // 60) % 60}</th> </tr> <tr> <td style="font-size: 15px; text-align: center"></td> <td style="font-size: 15px; text-align: center">Days</td> <td style="font-size: 15px; text-align: center"></td> <td style="font-size: 15px; text-align: center">Hours</td> <td style="font-size: 15px; text-align: center"></td> <td style="font-size: 15px; text-align: center">Minutes</td> </tr> </table> """ except FieldNotFound: return '<div style="text-align: center;">Duration is not available.</div>' except Exception as e: demisto.error(traceback.format_exc()) return_error(f"Error calculating duration\n{str(e)}")
def get_comments_command(issue_id): url = f'rest/api/latest/issue/{issue_id}/comment' result = jira_req('GET', url) body = result.json() comments = [] if body.get("comments"): for comment in body.get("comments"): comments.append({ 'Comment': comment.get("body"), 'User': demisto.get(comment, 'updateAuthor.name'), 'Created': comment.get("created") }) human_readable = tableToMarkdown("Comments", comments) contents = body outputs = {'Ticket(val.Id == obj.Id)': {'Id': issue_id, "Comment": comments}} return_outputs(readable_output=human_readable, outputs=outputs, raw_response=contents) else: demisto.results('No comments were found in the ticket')
def analyse_sample(): args = demisto.args() file_entry = args.get('file_id', '') if type(file_entry) in STRING_TYPES: file_entry = [f for f in file_entry.split(',') if f != ''] sample_url = args.get('sample_url', '') if type(sample_url) in STRING_TYPES: sample_url = [f for f in sample_url.split(',') if f != ''] internet_access = bool(strtobool(args.get('internet-access', 'true'))) should_wait = bool(strtobool(demisto.get(args, 'should_wait'))) comments = args.get('comments', '') systems = args.get('systems', '') if (len(file_entry) == 0 and len(sample_url) == 0) or ([] not in [file_entry, sample_url]): raise ValueError('You must specify one (and only one) of the following: sample_url, file_id.') LOG('analysing sample') if len(file_entry) != 0: return [analyse_sample_file_request(f, should_wait, internet_access, comments, systems) for f in file_entry] else: return [analyse_sample_url_request(s, should_wait, internet_access, comments, systems) for s in sample_url]
def fetch_incidents_command(): """ Fetch alerts from Canary Tools as incidents in Demisto last_fetch: The latest fetched alert creation time """ last_fetch = demisto.getLastRun().get('time') if last_fetch is None: last_fetch = parse_date_range(FETCH_DELTA, '%Y-%m-%d-%H:%M:%S')[0] # All alerts retrieved from get_alerts are newer than last_fetch and are in a chronological order alerts = get_alerts(last_fetch) incidents = [] current_fetch = last_fetch for alert in alerts: current_fetch = 1000 * (int(demisto.get(alert, 'description.created'))) current_fetch = timestamp_to_datestring(current_fetch, '%Y-%m-%d-%H:%M:%S') incident = create_incident(alert) incidents.append(incident) demisto.incidents(incidents) demisto.setLastRun({'time': current_fetch})
def create_incident_from_ticket(issue): labels = [ {'type': 'issue', 'value': json.dumps(issue)}, {'type': 'id', 'value': str(issue.get('id'))}, {'type': 'lastViewed', 'value': str(demisto.get(issue, 'fields.lastViewed'))}, {'type': 'priority', 'value': str(demisto.get(issue, 'fields.priority.name'))}, {'type': 'status', 'value': str(demisto.get(issue, 'fields.status.name'))}, {'type': 'project', 'value': str(demisto.get(issue, 'fields.project.name'))}, {'type': 'updated', 'value': str(demisto.get(issue, 'fields.updated'))}, {'type': 'reportername', 'value': str(demisto.get(issue, 'fields.reporter.displayName'))}, {'type': 'reporteremail', 'value': str(demisto.get(issue, 'fields.reporter.emailAddress'))}, {'type': 'created', 'value': str(demisto.get(issue, 'fields.created'))}, {'type': 'summary', 'value': str(demisto.get(issue, 'fields.summary'))}, {'type': 'description', 'value': str(demisto.get(issue, 'fields.description'))} ] name = demisto.get(issue, 'fields.summary') if name: name = f"Jira issue: {issue.get('id')}" severity = 0 if demisto.get(issue, 'fields.priority') and demisto.get(issue, 'fields.priority.name'): if demisto.get(issue, 'fields.priority.name') == 'Highest': severity = 4 elif demisto.get(issue, 'fields.priority.name') == 'High': severity = 3 elif demisto.get(issue, 'fields.priority.name') == 'Medium': severity = 2 elif demisto.get(issue, 'fields.priority.name') == 'Low': severity = 1 return { "name": name, "labels": labels, "details": demisto.get(issue, "fields.description"), "severity": severity, "rawJSON": json.dumps(issue) }
import demistomock as demisto from CommonServerPython import * from CommonServerUserPython import * import os import subprocess import sys import base64 # pylint: disable=E1103 reload(sys) sys.setdefaultencoding("utf-8") proxy = demisto.get(demisto.params(), "proxy") if proxy: http_proxy = os.environ["http_proxy"] https_proxy = os.environ["https_proxy"] return_code = 0 error_message = '' def rasterize_email_request(html, friendly_name): global return_code, error_message f = open('htmlBody.html', 'w') f.write('<html style="background:white";>' + html + '</html>') f.close() proxy_flag = "" if proxy:
def generate_md_context_get_issue(data): get_issue_obj: dict = {"md": [], "context": []} if not isinstance(data, list): data = [data] for element in data: md_obj, context_obj = {}, {} context_obj['Id'] = md_obj['id'] = demisto.get(element, 'id') context_obj['Key'] = md_obj['key'] = demisto.get(element, 'key') context_obj['Summary'] = md_obj['summary'] = demisto.get(element, 'fields.summary') context_obj['Status'] = md_obj['status'] = demisto.get(element, 'fields.status.name') assignee = demisto.get(element, 'fields.assignee') context_obj['Assignee'] = md_obj['assignee'] = "{name}({email})".format( name=assignee.get('displayName', 'null'), email=assignee.get('emailAddress', 'null') ) if assignee else 'null(null)' creator = demisto.get(element, 'fields.creator') context_obj['Creator'] = md_obj['creator'] = "{name}({email})".format( name=creator.get('displayName', 'null'), email=creator.get('emailAddress', 'null') ) if creator else 'null(null)' reporter = demisto.get(element, 'fields.reporter') md_obj['reporter'] = "{name}({email})".format( name=reporter.get('displayName', 'null'), email=reporter.get('emailAddress', 'null') ) if reporter else 'null(null)' md_obj.update({ 'issueType': demisto.get(element, 'fields.issuetype.description'), 'priority': demisto.get(element, 'fields.priority.name'), 'project': demisto.get(element, 'fields.project.name'), 'labels': demisto.get(element, 'fields.labels'), 'description': demisto.get(element, 'fields.description'), 'duedate': demisto.get(element, 'fields.duedate'), 'ticket_link': demisto.get(element, 'self'), 'created': demisto.get(element, 'fields.created'), }) attachments = demisto.get(element, 'fields.attachment') if isinstance(attachments, list): md_obj['attachment'] = ','.join(attach.get('filename') for attach in attachments) get_issue_obj['md'].append(md_obj) get_issue_obj['context'].append(context_obj) return get_issue_obj