def get_event_command(): # get event data from IronAPI event_id = demisto.getArg('event_id') results = IRON_DEFENSE.get_event(event_id) # Output the event data event = results.get('event') vue_markdown_link = IRON_DEFENSE.create_markdown_link("Open in IronVue", event.get("vue_url")) event_readable_output = tableToMarkdown(f'IronDefense Event: {event.get("category")} -' f' {event.get("sub_category")}\n' f'{vue_markdown_link}', event) return_outputs(readable_output=event_readable_output, outputs={ 'IronDefense.Event(val.id == obj.id)': event, }, raw_response=event) # Output each context table context_tables = results.get('context') for table in context_tables: if IRON_DEFENSE.event_context_table_contains_multi_columns(table): output_table = IRON_DEFENSE.event_context_table_to_dict_list(table) headers = [*output_table[0]] else: output_table = IRON_DEFENSE.event_context_table_to_dict(table) headers = [] return_outputs(readable_output=tableToMarkdown(f'Event Context: {table.get("name")}', output_table, headers=headers), outputs={ 'IronDefense.Event.Context(val.name == obj.name)': table, }, raw_response=table)
def test_tbl_to_md_empty_fields(): # all fields are empty data = [{ 'a': None, 'b': None, 'c': None, } for _ in range(3)] table_all_none = tableToMarkdown( 'tableToMarkdown test with all none fields', data) expected_table_all_none = '''### tableToMarkdown test with all none fields |a|b|c| |---|---|---| |||| |||| |||| ''' assert table_all_none == expected_table_all_none # all fields are empty - removed table_all_none2 = tableToMarkdown( 'tableToMarkdown test with all none fields2', data, removeNull=True) expected_table_all_none2 = '''### tableToMarkdown test with all none fields2 **No entries.** ''' assert table_all_none2 == expected_table_all_none2
def get_events_command(): alert_id = demisto.getArg('alert_id') limit = demisto.getArg('limit') offset = demisto.getArg('offset') results = IRON_DEFENSE.get_events(alert_id=alert_id, limit=limit, offset=offset) events = results.get('events') total_count = results.get('constraint').get('total') offset = results.get('constraint').get('offset') for i, event in enumerate(events): vue_markdown_link = IRON_DEFENSE.create_markdown_link("Open in IronVue", event.get("vue_url")) event_readable_output = tableToMarkdown(f'IronDefense Event {i + offset + 1}/{total_count}\n' f'{vue_markdown_link}', event) # Send each event return_outputs(readable_output=event_readable_output, outputs={ 'IronDefense.Event(val.id == obj.id)': event, }, raw_response=event) # Send constraints constraint = results.get('constraint') return_outputs(readable_output=tableToMarkdown('Query Constraints', constraint), outputs={ 'IronDefense.Query.GetEvents': constraint, }, raw_response=constraint)
def test_translate(requests_mock): """ Given An API key, and text to translate When Calling translate Then Test the command result structure """ from YodaSpeak import Client, translate_command client = Client(base_url=BASE_URL, verify=False, api_key='foo', proxy=False) args = {'text': 'this is some sentence for translation'} raw_response = util_load_json('test_data/translate.json') requests_mock.post(ENDPOINT_URL, json=raw_response) command_result = translate_command(client, **args) output = { 'Original': 'this is some sentence for translation', 'Translation': 'Some sentence for translation, this is.' } expected_result = CommandResults( outputs_prefix='YodaSpeak', outputs_key_field=f'{TRANSLATE_OUTPUT_PREFIX}.Original', outputs={TRANSLATE_OUTPUT_PREFIX: output}, raw_response=raw_response, readable_output=tableToMarkdown(name='Yoda Says...', t=output)) assert command_result.to_context() == expected_result.to_context()
def test_upload_valid_entry(mocker): """ Given: - Valid entry id of a file, str When: - When the user uploads a file for later conversion via entry Then: - Returns the response data """ client = create_client() mocker.patch.object( client, 'upload_entry_id', return_value=util_load_json('./test_data/upload_entry_response.json')) results = upload_command(client, {'entry_id': MOCK_ENTRY_ID}) raw_response = util_load_json('./test_data/upload_entry_response.json') raw_response['data']['operation'] = 'upload/entry' readable_output = tableToMarkdown( 'Upload Results', remove_empty_elements(raw_response.get('data')), headers=('id', 'operation', 'created_at', 'status'), headerTransform=string_to_table_header, ) assert results.outputs == remove_empty_elements(raw_response.get('data')) assert results.readable_output == readable_output
def test_convert_valid_format_and_id(mocker): """ Given: - Valid destination format for given file When: - When the user converts a file that was priorly uploaded Then: - Returns the response data """ client = create_client() mocker.patch.object( client, 'convert', return_value=util_load_json( 'test_data/convert_valid_format_and_id_response.json')) results = convert_command(client, { 'task_id': 'id', 'output_format': 'pdf' }) readable_output = tableToMarkdown( 'Convert Results', remove_empty_elements( util_load_json('test_data/convert_val' 'id_format_and_id_response.' 'json').get('data')), headers=('id', 'operation', 'created_at', 'status', 'depends_on_task_ids'), headerTransform=string_to_table_header) assert results.outputs == remove_empty_elements( util_load_json( 'test_data/convert_valid_format_and_id_response.json').get('data')) assert results.readable_output == readable_output
def test_get_indicators_command(mocker): """ Given: - Output of the feed API as list When: - Getting a limited number of indicators from the API Then: - Return results as war-room entry """ client = Client(base_url=URL) indicators_list = util_load_json( './test_data/build_iterator_results.json')[:10] mocker.patch.object(Client, 'build_iterator', return_value=indicators_list) results = get_indicators_command(client, params={ 'tlp_color': 'RED', 'create_relationships': 'false' }, args={'limit': '10'}) human_readable = tableToMarkdown('Indicators from HelloWorld Feed:', indicators_list, headers=['value', 'type'], headerTransform=string_to_table_header, removeNull=True) assert results.readable_output == human_readable
def test_tbl_to_md_no_header(): # no header table_no_headers = tableToMarkdown('tableToMarkdown test with no headers', DATA, headers=['no', 'header', 'found'], removeNull=True) expected_table_no_headers = '''### tableToMarkdown test with no headers **No entries.** ''' assert table_no_headers == expected_table_no_headers
def test_tbl_to_md_dict_with_special_character(): data = {'header_1': u'foo', 'header_2': [u'\xe2.rtf']} table_with_character = tableToMarkdown( 'tableToMarkdown test with special character', data) expected_string_with_special_character = '''### tableToMarkdown test with special character |header_1|header_2| |---|---| | foo | â.rtf | ''' assert table_with_character == expected_string_with_special_character
def test_tbl_to_md_header_with_special_character(): data = {'header_1': u'foo'} table_with_character = tableToMarkdown( 'tableToMarkdown test with special character Ù', data) expected_string_with_special_character = '''### tableToMarkdown test with special character Ù |header_1| |---| | foo | ''' assert table_with_character == expected_string_with_special_character
def test_tbl_to_md_only_data(): # sanity table = tableToMarkdown('tableToMarkdown test', DATA) expected_table = '''### tableToMarkdown test |header_1|header_2|header_3| |---|---|---| |a1|b1|c1| |a2|b2|c2| |a3|b3|c3| ''' assert table == expected_table
def test_tbl_to_md_list_of_strings_instead_of_dict(): # list of string values instead of list of dict objects table_string_array = tableToMarkdown('tableToMarkdown test with string array', ['foo', 'bar', 'katz'], ['header_1']) expected_string_array_tbl = '''### tableToMarkdown test with string array |header_1| |---| | foo | | bar | | katz | ''' assert table_string_array == expected_string_array_tbl
def test_tbl_to_md_single_column(): # single column table table_single_column = tableToMarkdown('tableToMarkdown test with single column', DATA, ['header_1']) expected_table_single_column = '''### tableToMarkdown test with single column |header_1| |---| | a1 | | a2 | | a3 | ''' assert table_single_column == expected_table_single_column
def index_doc_infos(doc_infos: List[DocInfo], link_prefix: str): if not doc_infos: return '' table_items = [] for d in doc_infos: table_items.append({ 'Name': f'[{d.name}]({link_prefix}/{d.id})', 'Description': d.description }) res = tableToMarkdown('', table_items, headers=['Name', 'Description']) return fix_mdx(res)
def test_tbl_to_md_string_header(): # string header (instead of list) table_string_header = tableToMarkdown('tableToMarkdown string header', DATA, 'header_1') expected_string_header_tbl = '''### tableToMarkdown string header |header_1| |---| | a1 | | a2 | | a3 | ''' assert table_string_header == expected_string_header_tbl
def test_tbl_to_md_list_of_strings_instead_of_dict_and_string_header(): # combination: string header + string values list table_string_array_string_header = tableToMarkdown('tableToMarkdown test with string array and string header', ['foo', 'bar', 'katz'], 'header_1') expected_string_array_string_header_tbl = '''### tableToMarkdown test with string array and string header |header_1| |---| | foo | | bar | | katz | ''' assert table_string_array_string_header == expected_string_array_string_header_tbl
def test_tbl_to_md_header_transform_underscoreToCamelCase(): # header transform table = tableToMarkdown('tableToMarkdown test with headerTransform', DATA, headerTransform=underscoreToCamelCase) expected_table = '''### tableToMarkdown test with headerTransform |Header1|Header2|Header3| |---|---|---| | a1 | b1 | c1 | | a2 | b2 | c2 | | a3 | b3 | c3 | ''' assert table == expected_table
def test_tbl_to_md_header_not_on_first_object(): # header not on first object data = copy.deepcopy(DATA) data[1]['extra_header'] = 'sample' table_extra_header = tableToMarkdown('tableToMarkdown test with extra header', data, headers=['header_1', 'header_2', 'extra_header']) expected_table_extra_header = '''### tableToMarkdown test with extra header |header_1|header_2|extra_header| |---|---|---| | a1 | b1 | | | a2 | b2 | sample | | a3 | b3 | | ''' assert table_extra_header == expected_table_extra_header
def test_tbl_to_md_dict_value(): # dict value data = copy.deepcopy(DATA) data[1]['extra_header'] = {'sample': 'qwerty', 'sample2': 'asdf'} table_dict_record = tableToMarkdown('tableToMarkdown test with dict record', data, headers=['header_1', 'header_2', 'extra_header']) expected_dict_record = '''### tableToMarkdown test with dict record |header_1|header_2|extra_header| |---|---|---| | a1 | b1 | | | a2 | b2 | sample: qwerty<br>sample2: asdf | | a3 | b3 | | ''' assert table_dict_record == expected_dict_record
def test_tbl_to_md_url(): # url + empty data data = copy.deepcopy(DATA) for i, d in enumerate(data): d['header_3'] = '[url](https:\\demisto.com)' d['header_2'] = None table_url_missing_info = tableToMarkdown('tableToMarkdown test with url and missing info', data) expected_table_url_missing_info = '''### tableToMarkdown test with url and missing info |header_1|header_2|header_3| |---|---|---| | a1 | | [url](https:\\demisto.com) | | a2 | | [url](https:\\demisto.com) | | a3 | | [url](https:\\demisto.com) | ''' assert table_url_missing_info == expected_table_url_missing_info
def test_tbl_to_md_multiline(): # escaping characters: multiline + md-chars data = copy.deepcopy(DATA) for i, d in enumerate(data): d['header_2'] = 'b%d.1\nb%d.2' % (i + 1, i + 1,) d['header_3'] = 'c%d|1' % (i + 1,) table = tableToMarkdown('tableToMarkdown test with multiline', data) expected_table = '''### tableToMarkdown test with multiline |header_1|header_2|header_3| |---|---|---| | a1 | b1.1<br>b1.2 | c1\|1 | | a2 | b2.1<br>b2.2 | c2\|1 | | a3 | b3.1<br>b3.2 | c3\|1 | ''' assert table == expected_table
def test_tbl_to_md_list_values(): # list values data = copy.deepcopy(DATA) for i, d in enumerate(data): d['header_3'] = [i + 1, 'second item'] d['header_2'] = 'hi' table_list_field = tableToMarkdown('tableToMarkdown test with list field', data) expected_table_list_field = '''### tableToMarkdown test with list field |header_1|header_2|header_3| |---|---|---| | a1 | hi | 1,<br>second item | | a2 | hi | 2,<br>second item | | a3 | hi | 3,<br>second item | ''' assert table_list_field == expected_table_list_field
def test_check_status_valid_id_download(mocker, create_war_room_entry): """ Given: - A valid task id, of an download operation When: - When the user checks the status of a task that was priorly done, and it is an download operation. Then: - When checking on a download operation and the argument 'create_war_room_entry' is set to True, the output is a warroom entry. if set to False, then a regular response is retrieved. """ import CloudConvert client = create_client() mocker.patch.object(client, 'check_status', return_value=util_load_json( 'test_data/check_status_download_response.json')) mocker.patch.object(client, 'get_file_from_url', return_value='') file_name = util_load_json('test_data/check_status_download_response.json').get('data'). \ get('result').get('files')[0].get('filename') mocker.patch.object(CloudConvert, 'fileResult', return_value={'File': file_name}) results = check_status_command( client, { 'task_id': 'id', 'create_war_room_entry': create_war_room_entry }) raw_response_data = util_load_json( 'test_data/check_status_download_response.json').get('data') modify_results_dict(raw_response_data) if create_war_room_entry: raw_response_data['operation'] = 'download/entry' assert results.get('File') == file_name else: raw_response_data['operation'] = 'download/url' assert results.outputs == remove_empty_elements(raw_response_data) readable_output = tableToMarkdown( 'Check Status Results', remove_empty_elements(raw_response_data), headers=('id', 'operation', 'created_at', 'status', 'depends_on_task_ids', 'file_name', 'url'), headerTransform=string_to_table_header) assert results.readable_output == readable_output
def index_doc_infos(doc_infos: List[DocInfo], link_prefix: str, headers: Optional[Tuple[str, str]] = None): if not headers: headers = ('Name', 'Description') if not doc_infos: return '' table_items = [] for d in doc_infos: name = html.escape(d.name) link_name = f'[{name}]({link_prefix}/{d.id})' for word in re.split(r'\s|-', name): if len(word ) > 25: # we have a long word tell browser ok to break it link_name = '<span style={{wordBreak: "break-word"}}>' + link_name + '</span>' break table_items.append({headers[0]: link_name, headers[1]: d.description}) res = tableToMarkdown('', table_items, headers=headers) return fix_mdx(res)
def test_download_valid_id(mocker, download_as): """ Given: - Valid task id for given file When: - When the user wants to download a file that was priorly uploaded Then: - Returns the response message of invalid input """ client = create_client() mocker.patch.object(client, 'download_url', return_value=util_load_json( 'test_data/download_valid_id_response.json')) results = download_command(client, { 'task_id': 'id', 'download_as': download_as }) raw_response = util_load_json('test_data/download_valid_id_response.json') if download_as == 'url': raw_response['data']['operation'] = 'download/url' readable_output = tableToMarkdown( 'Download Results', remove_empty_elements(raw_response.get('data')), headers=('id', 'operation', 'created_at', 'status', 'depends_on_task_ids'), headerTransform=string_to_table_header, ) assert results.outputs == remove_empty_elements( raw_response.get('data')) assert results.readable_output == readable_output else: raw_response['data']['operation'] = 'download/entry' assert results.outputs == remove_empty_elements( raw_response.get('data'))
def test_check_status_valid_id_non_download(mocker, create_war_room_entry): """ Given: - A valid task id, of a non-download operation When: - When the user checks the status of a task that was priorly done, and it is not download. the purpose here is to make sure that the extra argument, 'create_war_room_entry', only makes a difference when the id is of an actual download operation. Then: - Returns the response """ client = create_client() mocker.patch.object( client, 'check_status', return_value=util_load_json( 'test_data/check_status_non_download_response.json')) results = check_status_command( client, { 'task_id': 'id', 'create_war_room_entry': create_war_room_entry }) raw_response_data = util_load_json( 'test_data/check_status_non_download_response.json').get('data') modify_results_dict(raw_response_data) readable_output = tableToMarkdown('Check Status Results', remove_empty_elements(raw_response_data), headers=('id', 'operation', 'created_at', 'status', 'depends_on_task_ids', 'file_name', 'url'), headerTransform=string_to_table_header) modify_results_dict(raw_response_data) assert results.outputs == remove_empty_elements(raw_response_data) assert results.readable_output == readable_output
def get_alerts_command(): alert_id = demisto.getArg('alert_id') category = demisto.getArg('category') sub_category = demisto.getArg('sub_category') status = demisto.getArg('status') analyst_severity = demisto.getArg('analyst_severity') analyst_expectation = demisto.getArg('analyst_expectation') min_severity = demisto.getArg('min_severity') max_severity = demisto.getArg('max_severity') min_created = demisto.getArg('min_created') max_created = demisto.getArg('max_created') min_updated = demisto.getArg('min_updated') max_updated = demisto.getArg('max_updated') min_first_event_created = demisto.getArg('min_first_event_created') max_first_event_created = demisto.getArg('max_first_event_created') min_last_event_created = demisto.getArg('min_last_event_created') max_last_event_created = demisto.getArg('max_last_event_created') min_first_event_start_time = demisto.getArg('min_first_event_start_time') max_first_event_start_time = demisto.getArg('max_first_event_start_time') min_last_event_end_time = demisto.getArg('min_last_event_end_time') max_last_event_end_time = demisto.getArg('max_last_event_end_time') analytic_version = demisto.getArg('analytic_version') limit = demisto.getArg('limit') offset = demisto.getArg('offset') sort = demisto.getArg('sort') results = IRON_DEFENSE.get_alerts(alert_id=alert_id, category=category, sub_category=sub_category, status=status, analyst_severity=analyst_severity, analyst_expectation=analyst_expectation, min_severity=min_severity, max_severity=max_severity, min_created=min_created, max_created=max_created, min_updated=min_updated, max_updated=max_updated, min_first_event_created=min_first_event_created, max_first_event_created=max_first_event_created, min_last_event_created=min_last_event_created, max_last_event_created=max_last_event_created, min_first_event_start_time=min_first_event_start_time, max_first_event_start_time=max_first_event_start_time, min_last_event_end_time=min_last_event_end_time, max_last_event_end_time=max_last_event_end_time, analytic_version=analytic_version, limit=limit, offset=offset, sort=sort) alerts = results.get('alerts') total_count = results.get('constraint').get('total') offset = results.get('constraint').get('offset') for i, alert in enumerate(alerts): # Send each alert vue_markdown_link = IRON_DEFENSE.create_markdown_link("Open in IronVue", alert.get("vue_url")) alert_readable_output = tableToMarkdown(f'IronDefense Alert {i + offset + 1}/{total_count}: {alert.get("category")} -' f' {alert.get("sub_category")}\n' f'{vue_markdown_link}', alert) return_outputs(readable_output=alert_readable_output, outputs={ 'IronDefense.Alert(val.id == obj.id)': alert, }, raw_response=alert) # Send constraints constraint = results.get('constraint') return_outputs(readable_output=tableToMarkdown('Query Constraints', constraint), outputs={ 'IronDefense.Query.GetAlerts': constraint, }, raw_response=constraint)
def get_alert_irondome_information_command(): alert_id = demisto.getArg('alert_id') results = IRON_DEFENSE.get_alert_irondome_information(alert_id) if len(results.get('correlations')) == 0 and \ len(results.get('correlation_participation')) == 0 and \ len(results.get('community_comments')) == 0 and \ len(results.get('dome_notifications')) == 0: demisto.results(f'No correlations found for alert ID: {alert_id}') return # Output correlations correlations = results.get('correlations') for correlation in correlations: dome_tag = correlation.get('dome_tag') correlation_data = correlation.get('correlations') output = { 'alert_id': alert_id, 'correlation': correlation } ip_correlations = list(filter(lambda corr: corr.get('ip') is not None, correlation_data)) domain_correlations = list(filter(lambda corr: corr.get('domain') is not None, correlation_data)) behavior_correlations = list(filter(lambda corr: corr.get('behavior') is not None, correlation_data)) if len(ip_correlations) != 0: return_outputs(readable_output=tableToMarkdown(f'IronDome IP Correlations in "{dome_tag}"', ip_correlations, headers=[*ip_correlations[0]]), outputs={ 'IronDome.Correlations(val.alert_id = obj.alert.id)': output }, raw_response=correlation) if len(domain_correlations) != 0: return_outputs(readable_output=tableToMarkdown(f'IronDome Domain Correlations in "{dome_tag}"', domain_correlations, headers=[*domain_correlations[0]]), outputs={ 'IronDome.Correlations(val.alert_id = obj.alert.id)': output }, raw_response=correlation) if len(behavior_correlations) != 0: return_outputs(readable_output=tableToMarkdown(f'IronDome Behavior Correlations in "{dome_tag}"', behavior_correlations, headers=[*behavior_correlations[0]]), outputs={ 'IronDome.Correlations(val.alert_id = obj.alert.id)': output }, raw_response=correlation) # Output correlation participation correlation_participation = results.get('correlation_participation') for participant in correlation_participation: dome_tag = participant.get('dome_tag') output = { 'alert_id': alert_id, 'correlation_participation': participant } table_data = [] # append each correlation context to display in the table, if it exists behavior = participant.get('behavior') if behavior is not None: table_data.append(behavior) domain = participant.get('behavior') if domain is not None: table_data.append(domain) ip = participant.get('ip') if ip is not None: table_data.append(ip) # Send the participant info return_outputs(readable_output=tableToMarkdown(f'IronDome Correlation Participation in "{dome_tag}"', table_data, headers=[*table_data[0]]), outputs={ 'IronDome.CorrelationParticipation(val.alert_id = obj.alert.id)': output }, raw_response=participant) # Output comments community_comments = results.get('community_comments') community_comments_output = { 'alert_id': alert_id, 'community_comments': community_comments, } if len(community_comments) > 0: return_outputs(readable_output=tableToMarkdown('IronDome Community Comments', community_comments, headers=[*community_comments[0]]), outputs={ 'IronDome.CommunityComments(val.alert_id = obj.alert.id)': community_comments_output }, raw_response=community_comments) # Output cognitive system score cognitive_system_score = results.get('cognitive_system_score') cognitive_system_score_output = { 'alert_id': alert_id, 'cognitive_system_score': cognitive_system_score, } return_outputs(readable_output=f'### Cognitive System Score: {cognitive_system_score}', outputs={ 'IronDome.CognitiveSystemScore(val.alert_id = obj.alert.id)': cognitive_system_score_output }, raw_response=cognitive_system_score) # Output dome notifications dome_notifications = results.get('dome_notifications') for notification in dome_notifications: category = notification.get('category') output = { 'alert_id': alert_id, 'dome_notification': notification } return_outputs(readable_output=tableToMarkdown(f'IronDome Notification: {category}', notification), outputs={ 'IronDome.Notification(val.alert_id = obj.alert.id)': output }, raw_response=notification) return_outputs(readable_output=IRON_DEFENSE.create_dome_markdown_link('Open IronDome information in IronVue', alert_id), outputs={})