DBotSuggestClassifierMapping Deprecated
Deprecated. No available replacement. Suggests a classifier mapping based on an advanced name matching algorithm.
python · Base
Source
from CommonServerPython import * import itertools import numbers import re import socket from collections import Counter from collections import OrderedDict from datetime import datetime, timedelta INCIDENT_FIELD_NAME = "name" INCIDENT_FIELD_MACHINE_NAME = "cliName" INCIDENT_FIELD_SYSTEM = "system" INCIDENT_FIELD_UNMAPPED = "unmapped" SAMPLES_INCOMING = 'incomingSamples' SAMPLES_SCHEME = 'scheme' SAMPLES_OUTGOING = 'outgoingSamples' COUNT_KEYWORD = "count" SIEM_FIELDS = {'Account ID': {'aliases': ['accountid', 'account id'], 'validators': []}, 'Account Name': {'aliases': ['accountname', 'account name'], 'validators': ['validate_alphanumeric_with_common_punct']}, 'Account Type': {'aliases': ['accounttype', 'account type'], 'validators': ['validate_alphanumeric_with_common_punct']}, 'Agent ID': {'aliases': ['agentid', 'agent id', 'sensor id', 'tenant id'], 'validators': []}, 'Tenant Name': {'aliases': ['tenant name', 'tenant name'], 'validators': ['validate_alphanumeric_with_common_punct']}, 'App': {'aliases': ['app', 'app'], 'validators': ['validate_alphanumeric_with_common_punct']}, 'Attachment Name': {'aliases': ['attachmentname', 'attachment name'], 'validators': ['validate_alphanumeric_with_common_punct']}, 'Blocked Action': {'aliases': ['blockedaction', 'blocked action', 'prevention mode'], 'validators': ['validate_alphanumeric_with_common_punct']}, 'City': {'aliases': ['city'], 'validators': ['validate_alphanumeric_with_common_punct']}, 'Command Line': {'aliases': ['commandline', 'command line', 'cmdline', 'cmd line', 'process file name', 'process file path', 'process full path', 'process full path', 'cmd'], 'validators': ['validate_file_full_path']}, 'Event ID': {'aliases': ['eventid', 'event id', 'alert id', 'offense id'], 'validators': ['validate_alphanumeric_with_common_punct']}, 'Event Type': {'aliases': ['eventtype', 'event type', 'alert type'], 'validators': ['validate_alphanumeric_with_common_punct']}, 'Company Name': {'aliases': ['companyname', 'company name', 'company', 'customer'], 'validators': ['validate_alphanumeric_with_common_punct']}, 'Country': {'aliases': ['country', 'country name'], 'validators': ['validate_alphanumeric_with_common_punct']}, 'Critical Assets': {'aliases': ['criticalassets', 'critical assets'], 'validators': ['validate_alphanumeric_with_common_punct']}, 'Description': {'aliases': ['description'], 'validators': ['validate_alphanumeric_with_common_punct']}, 'Destination IP': {'aliases': ['destinationip', 'destination ip', 'destination address', 'dest ip', 'dest address', 'target address', 'dst'], 'validators': ['validate_ip']}, 'Destination Port': {'aliases': ['destinationport', 'destination port', 'dst port', 'dest port'], 'validators': ['validate_number']}, 'Email BCC': {'aliases': ['emailbcc', 'email bcc', 'bcc recipient', 'bcc'], 'validators': ['validate_email']}, 'Email Body': {'aliases': ['emailbody', 'email body', 'body'], 'validators': []}, 'Email Body Format': {'aliases': ['emailbodyformat', 'email body format', 'body type', 'body content type'], 'validators': []}, 'Email Body HTML': {'aliases': ['emailbodyhtml', 'email body html'], 'validators': []}, 'Email CC': {'aliases': ['emailcc', 'email cc', 'cc recipient', 'cc'], 'validators': ['validate_email']}, 'Email From': {'aliases': ['emailfrom', 'email from', 'from'], 'validators': ['validate_email']}, 'Email HTML': {'aliases': ['emailhtml', 'email html'], 'validators': []}, 'Email Headers': {'aliases': ['emailheaders', 'email headers', 'headers', 'message headers', 'internet message header'], 'validators': ['']}, 'Email In Reply To': {'aliases': ['emailinreplyto', 'email in reply to'], 'validators': []}, 'Email Received': {'aliases': ['emailreceived', 'email received', 'received date time', 'received time'], 'validators': ['validate_date']}, 'Email Reply To': {'aliases': ['emailreplyto', 'email replay to', 'reply to'], 'validators': []}, 'Email Sender IP': {'aliases': ['emailsenderip', 'email sender ip'], 'validators': ['validate_ip']}, 'Email Size': {'aliases': ['emailsize', 'email size'], 'validators': ['validate_number']}, 'Email Subject': {'aliases': ['emailsubject', 'email subject', 'subject'], 'validators': []}, 'Email To': {'aliases': ['emailto', 'email to', 'to recipients', 'recipients', 'recipient'], 'validators': ['validate_email']}, 'File Hash': {'aliases': ['filehash', 'file hash', 'event file hash', 'md5', 'sha1', 'sha256'], 'validators': ['validate_hash']}, 'File Name': {'aliases': ['filename', 'file name'], 'validators': []}, 'File Path': {'aliases': ['filepath', 'file path', 'full path', 'full path'], 'validators': ['validate_file_full_path']}, 'File Size': {'aliases': ['filesize', 'file size'], 'validators': ['validate_number']}, 'File Type': {'aliases': ['filetype', 'file type'], 'validators': ['validate_alphanumeric_with_common_punct']}, 'Source Hostname': { 'aliases': ['source hostname', 'source host name', 'src hostname', 'src host name'], 'validators': ['validate_hostname']}, 'Destination Hostname': { 'aliases': ['destination hostname', 'destination host name', 'dest hostname', 'dest host name', 'dst hostname', 'dst host name', 'target hostname', 'target host name'], 'validators': ['validate_hostname']}, 'Source Network': {'aliases': ['source network', 'sourcenetwork', 'src network'], 'validators': ['validate_alphanumeric_with_common_punct']}, 'Destination Network': {'aliases': ['destination network', 'destinationnetwork', 'dest network', 'dst network', 'target netwrok'], 'validators': ['validate_alphanumeric_with_common_punct']}, 'Device Name': { 'aliases': ['devicename', 'device name', 'endpoint name', 'end point name'], 'validators': ['validate_alphanumeric_with_common_punct']}, 'MAC Address': {'aliases': ['macaddress', 'mac address', 'mac', 'src mac', 'source mac'], 'validators': ['validate_mac']}, 'PID': {'aliases': ['pid', 'process pid', 'parent process pid', 'target process pid'], 'validators': ['validate_number']}, 'Parent Process ID': {'aliases': ['parentprocessid', 'parent process id'], 'validators': ['validate_number']}, 'Region': {'aliases': ['region', 'region'], 'validators': ['validate_alphanumeric_with_common_punct']}, 'Signature': {'aliases': ['signature', 'signature'], 'validators': []}, 'Source IP': { 'aliases': ['sourceip', 'source ip', 'src ip', 'src address', 'source address', 'computer ip', 'device ip', 'attacker address', 'attacker ip', 'sender ip', 'sender address', 'agent ip'], 'validators': ['validate_ip']}, 'Source Port': {'aliases': ['sourceport', 'source port', 'src port'], 'validators': ['validate_number']}, 'OS': {'aliases': ['operating system', 'os type', 'os version', 'os'], 'validators': []}, 'Subtype': {'aliases': ['subtype', 'subtype'], 'validators': ['validate_alphanumeric_with_common_punct']}, 'Terminated Action': {'aliases': ['terminatedaction', 'terminated action'], 'validators': []}, 'Traps ID': {'aliases': ['trapsid', 'traps id', 'trap id'], 'validators': []}, 'Source Username': {'aliases': ['username', 'username', 'user name', 'src user name', 'src username', 'source username', 'source user name'], 'validators': ['validate_alphanumeric_with_common_punct']}, 'Destination Username': {'aliases': ['destination username', 'destination user name', 'dest username', 'dest user name', 'dst username', 'dst user name', 'target user name', 'target username'], 'validators': ['validate_alphanumeric_with_common_punct']}, 'Detection URL': {'aliases': ['detection url'], 'validators': ['validate_url']}, 'Vendor ID': {'aliases': ['vendorid', 'vendor id'], 'validators': []}, 'Vendor Product': {'aliases': ['vendorproduct', 'vendor product'], 'validators': ['validate_alphanumeric_with_common_punct']}, 'category': {'aliases': ['category', 'category'], 'validators': ['validate_alphanumeric_with_common_punct']}, 'details': {'aliases': ['details', 'description'], 'validators': ['validate_alphanumeric_with_common_punct']}, 'name': {'aliases': ['name', 'Name', 'alert name', 'event name', 'rule name', 'title'], 'validators': ['validate_alphanumeric_with_common_punct', 'extact_name_math']}, 'occurred': {'aliases': ['occurred', 'occured', 'occurred time', 'event start time', 'event at', 'event time', 'start time', 'create time', 'timestamp', 'unix time', 'click time'], 'validators': ['validate_date']}, 'owner': {'aliases': ['owner'], 'validators': []}, 'severity': {'aliases': ['event severity', 'severity', 'event priority', 'priority', 'urgency'], 'validators': []}, 'Log Source': {'aliases': ['log source', 'log sources', 'logsource'], 'validators': []}, 'Protocol': {'aliases': ['protocol'], 'validators': []}, } suffix_mapping = { 'ing': '', 'ly': '', 'ed': '', 'ious': '', 'ies': 'y', 'ive': '', 'es': '', 's': '' } class DateValidator: def __init__(self): year_options = ['%y', '%Y'] months_options = ['%m', '%B'] day_options = ['%d'] delimeters_options = [".", "-", "/", "\\"] self.common_separators = [' ', 'T', ','] date_formats_options = [] # type: List[tuple] for delimeter in delimeters_options: delimeters = [delimeter] date_formats_options += list( itertools.product(year_options, delimeters, months_options, delimeters, day_options)) date_formats_options += list( itertools.product(year_options, delimeters, day_options, delimeters, months_options)) date_formats_options += list( itertools.product(day_options, delimeters, months_options, delimeters, year_options)) date_formats_options += list( itertools.product(day_options, delimeters, months_options, delimeters, year_options)) self.date_formats_options = list(map(lambda x: "".join(x), date_formats_options)) def try_parsing_date(self, text): for fmt in self.date_formats_options: try: return datetime.strptime(text, fmt) except ValueError: pass return None def has_valid_date(self, text): parts = [] # type: List[str] for sep in self.common_separators: parts += text.split(sep) return any(map(lambda x: self.try_parsing_date(x) is not None, parts)) @staticmethod def is_datetime_last_years(d, number_of_years=3): if d is not None: now = datetime.now() return now - timedelta(days=365 * number_of_years) <= d <= now + timedelta(days=365 * number_of_years) return False @staticmethod def safe_parse_timestamp(value): try: d = datetime.fromtimestamp(int(value)) return d except Exception: return None def is_unix_timestamp(self, value): try: value = int(value) return self.is_datetime_last_years(self.safe_parse_timestamp(value)) or self.is_datetime_last_years( self.safe_parse_timestamp(value / 1000)) except Exception: return False class Validator: def __init__(self): self.EMAIL_REGEX = re.compile('^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$') self.NUMBER_REGEX = re.compile('^([0-9]+)$') self.SHA256_REGEX = re.compile('^[A-Fa-f0-9]{64}$') self.MD5_REGEX = re.compile('^[a-fA-F0-9]{32}$') self.HASH_REGEX = re.compile('^[a-fA-F0-9]+$') self.MAC_REGEX = re.compile('^[0-9a-f]{2}([-:]?)[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$', re.IGNORECASE) self.URL_REGEX = re.compile( r'^(?:http|ftp|hxxp)s?://' # http:// or https:// r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # domain... r'localhost|' # localhost... r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip r'(?::\d+)?' # optional port r'(?:/?|[/?]\S+)$', re.IGNORECASE) self.COMMON_NAME_CHARECTERS = re.compile('^[0-9a-zA-Z"\s_\-\'./]+$') self.HOSTNAME_PART_REGEX = re.compile('(?!-)[A-Z\d-]{1,63}(?<!-)$') self.FULL_FILE_PATH_REGEX = re.compile('^((?:/[^/\n]+)*|.*(\\\\.*))$') self.date_validator = DateValidator() def validate_regex(self, pattern, value, json_field_name=None): if isinstance(value, str): return pattern.match(value) is not None return False def validate_ip(self, field_name, value, json_field_name=None): try: socket.inet_aton(value) return True except socket.error: return False def validate_url(self, field_name, value, json_field_name=None): return self.validate_regex(self.URL_REGEX, value) def validate_email(self, field_name, value, json_field_name=None): return self.validate_regex(self.EMAIL_REGEX, value) def validate_number(self, field_name, value, json_field_name=None): if isinstance(value, numbers.Number): return True else: return self.validate_regex(self.NUMBER_REGEX, value, json_field_name=None) def validate_not_count(self, field_name, value): is_count = COUNT_KEYWORD in field_name.lower() and self.validate_number(field_name, value) return not is_count def validate_sha256(self, field_name, value, json_field_name=None): return self.validate_regex(self.SHA256_REGEX, value) def validate_md5(self, field_name, value, json_field_name=None): return self.validate_regex(self.MD5_REGEX, value) def validate_hash(self, field_name, value, json_field_name=None): return self.validate_regex(self.HASH_REGEX, value) and len(value) % 2 == 0 def validate_mac(self, field_name, value, json_field_name=None): return self.validate_regex(self.MAC_REGEX, value) def validate_hostname(self, field_name, hostname, json_field_name=None): if not isinstance(hostname, str) or len(hostname) > 255: # type: ignore return False if hostname[-1] == ".": # type: ignore hostname = hostname[:-1] # type: ignore allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE) return all(allowed.match(x) for x in hostname.split(".")) def validate_date(self, field_name, value, json_field_name=None): if self.validate_number("", value): return self.date_validator.is_unix_timestamp(value) else: return self.date_validator.has_valid_date(value) def validate_alphanumeric_with_common_punct(self, field_name, value, json_field_name=None): return self.validate_regex(self.COMMON_NAME_CHARECTERS, value) def validate_file_full_path(self, field_name, value, json_field_name=None): return self.validate_regex(self.FULL_FILE_PATH_REGEX, value) def extact_name_math(self, field_name, value, json_field_name=None): return field_name == json_field_name def validate(self, validator_name, field_name, value, json_field_name=None): validate_func = getattr(self, validator_name) return validate_func(field_name, value, json_field_name) def is_sublist_of_list(s, lst): sub_set = False if s == []: sub_set = True elif s == lst: sub_set = True elif len(s) > len(lst): sub_set = False else: for i in range(len(lst)): if lst[i] == s[0]: n = 1 while (n < len(s)) and (i + n) < len(lst) and (lst[i + n] == s[n]): n += 1 if n == len(s): sub_set = True return sub_set def lemma_word(word): for suffix in suffix_mapping: if word.endswith(suffix): candidate = word[:-len(suffix)] + suffix_mapping[suffix] if candidate in ALL_POSSIBLE_TERMS_SET or candidate.lower() in ALL_POSSIBLE_TERMS_SET: return candidate.lower() return word.lower() def remove_dups(seq): return list(OrderedDict.fromkeys(seq)) def split_by_non_alpha_numeric(_string): return list(filter(lambda x: x, re.split('[^a-zA-Z0-9]', _string))) def camel_case_split(identifier): matches = re.finditer('.+?(?:(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|$)', identifier) return [m.group(0) for m in matches] def flatten_json(y): out = {} has_more_than_one_value = [] delimeter = '.' def flatten(x, name=''): if type(x) is dict: for a in x: flatten(x[a], name + a + delimeter) elif type(x) is list and len(x) > 0 and type(x) is not dict: i = 0 for a in x: flatten(a, name + "[" + str(i) + "]" + delimeter) i += 1 if i > 1: has_more_than_one_value.append(name[:-1]) else: out[name[:-1]] = x flatten(y) return out, has_more_than_one_value def number_of_terms(value): return len(value.split(" ")) def normilize(value): parts = [] # type: List[str] for part in split_by_non_alpha_numeric(value): parts += camel_case_split(part) terms = list(map(lemma_word, parts)) return remove_dups(terms) def validate_value_with_validator(alias, value, json_field_name=None): field_name = ALIASING_MAP[alias] validators = SIEM_FIELDS[field_name]['validators'] # type: ignore validators = [v for v in validators if v] if len(validators) == 0: return True validator_results = [] for validator_name in validators: validator_results.append(VALIDATOR.validate(validator_name, alias, value, json_field_name)) return all(validator_results) def get_candidates(json_field_name): json_field_terms = normilize(json_field_name) aliases_terms = ALIASING_TERMS_MAP.items() match_terms = list(map(lambda x: x[0], filter(lambda alias_terms: is_sublist_of_list(alias_terms[1], json_field_terms), aliases_terms))) return sorted(match_terms, reverse=True, key=number_of_terms) def suggest_field_with_alias(json_field_name, json_field_value=None): norm_json_field_name = " ".join(normilize(json_field_name)) candidates = get_candidates(json_field_name) if json_field_value is not None: candidates = list(filter(lambda c: validate_value_with_validator(c, json_field_value, norm_json_field_name), candidates)) if len(candidates) > 0: alias = candidates[0] return ALIASING_MAP[alias], alias return None, None def suggest_field(json_field_name, json_field_value=None): return suggest_field_with_alias(json_field_name, json_field_value)[0] def generate_aliases(field): aliases = [] aliases.append(field.lower()) aliases.append(" ".join(normilize(field))) aliases.append("".join(normilize(field))) return aliases def get_aliasing(siem_fields): aliasing_map = {} aliases_terms_map = {} for field, data in siem_fields.items(): for alias in data['aliases']: aliasing_map[alias] = field aliases_terms_map[alias] = alias.split(" ") return aliasing_map, aliases_terms_map def is_value_substring_of_one_values(value, all_values): return any(map(lambda field: value in field, all_values)) def get_alias_index(field_name, alias): return SIEM_FIELDS[field_name]['aliases'].index(alias) # type: ignore def get_most_relevant_json_field(field_name, json_field_to_alias): if len(json_field_to_alias) == 0: return # calculate jaccard score for each alias, and get the candidates with max score scores = {} for json_field, alias in json_field_to_alias.items(): scores[json_field] = jaccard_similarity_for_string_terms(json_field, alias) scores = {k: v for k, v in scores.items() if v == max(scores.values())} # calculate jaccard score for each field, and get the candidates with max score for json_field, alias in json_field_to_alias.items(): scores[json_field] = jaccard_similarity_for_string_terms(json_field, field_name) scores = {k: v for k, v in scores.items() if v == max(scores.values())} # for candidates with the same score with the least alias index candidates = sorted(list(scores.keys()), key=lambda json_field: get_alias_index(field_name, json_field_to_alias[json_field])) return candidates[0] def match_for_incident(incident_to_match): flat_incident, more_than_one_field_items = flatten_json(incident_to_match) incident = {k: v for k, v in flat_incident.items() if not is_value_substring_of_one_values(k, more_than_one_field_items)} if SCHEME_ONLY: incident = {k: v for k, v in incident.items() if not k.endswith(COUNT_KEYWORD)} else: incident = {k: v for k, v in incident.items() if v is not None and VALIDATOR.validate_not_count(k, v)} mapping = {} # type: ignore for json_field_name, json_field_value in incident.items(): # we try to get suggestion if it's scheme or if the value is not empty if SCHEME_ONLY or json_field_value: incident_field_suggestion, alias = suggest_field_with_alias(json_field_name, json_field_value) if incident_field_suggestion: if incident_field_suggestion not in mapping: mapping[incident_field_suggestion] = {} mapping[incident_field_suggestion][json_field_name] = alias return {incident_field_name: get_most_relevant_json_field(incident_field_name, json_field_to_alias) for incident_field_name, json_field_to_alias in mapping.items()} def jaccard_similarity(list1, list2): intersection = len(list(set(list1).intersection(list2))) union = (len(list1) + len(list2)) - intersection return float(intersection) / union def jaccard_similarity_for_string_terms(str1, str2): return jaccard_similarity(normilize(str1), normilize(str2)) def get_most_relevant_match_for_field(field_name, cnt): # return exact match if field_name in cnt: return field_name suggestions_with_jaccard_score = [(suggestion, jaccard_similarity_for_string_terms(field_name, suggestion)) for suggestion in cnt.keys()] suggestions_with_jaccard_score = sorted(suggestions_with_jaccard_score, key=lambda x: x[1], reverse=True) # check for extact terms if suggestions_with_jaccard_score[0][1] == 1: return suggestions_with_jaccard_score[0][0] # if we have only scheme or all the values are the same if SCHEME_ONLY or len(set(cnt.values())) == 1: return suggestions_with_jaccard_score[0][0] return cnt.most_common()[0][0] def match_for_incidents(incidents_to_match): fields_cnt = {} # type: Dict[str, Counter] for flat_incident in incidents_to_match: for k, v in match_for_incident(flat_incident).items(): if k not in fields_cnt: fields_cnt[k] = Counter() if v: fields_cnt[k][v] += 1 mapping_result = {field_name: get_most_relevant_match_for_field(field_name, field_cnt) for field_name, field_cnt in fields_cnt.items()} return mapping_result def format_value_to_mapper(json_field): parts = json_field.split('.', 1) root = parts[0] accessor = "" if len(parts) > 1: accessor = parts[1] res = { "simple": "", "complex": { "root": root, "accessor": accessor, "filters": [], "transformers": [] } } return res def format_incident_field_to_mapper(incident_field_name, field_name_to_machine_name): res = { "simple": "", "complex": { "root": field_name_to_machine_name[incident_field_name], "accessor": "", "filters": [], "transformers": [] } } return res def verify_non_empty_values_in_incidents(expression, incidents): for incident in incidents: res = demisto.dt(incident, expression) if res: return True return False def get_complex_value_key(complex_value): if 'complex' in complex_value: complex_value = complex_value['complex'] readable_value = complex_value.get('root') if complex_value.get('accessor'): readable_value += "." + complex_value.get('accessor') return readable_value def combine_mappers(original_mapper, new_mapper, incidents): mapper = new_mapper if original_mapper: mapper.update(original_mapper) return mapper def filter_by_dict_by_keys(_dict, keys): return {k: v for k, v in _dict.items() if k in keys} def parse_incident_sample(sample): if type(sample) is dict and 'rawJSON' in sample: incident = json.loads(sample['rawJSON']) else: try: incident = json.loads(sample) except Exception: incident = sample return incident SCHEME_ONLY = False VALIDATOR = Validator() ALIASING_MAP, ALIASING_TERMS_MAP, FIELD_NAME_TO_CLI_NAME = {}, {}, {} ALL_POSSIBLE_TERMS_SET = set() def init(): global SCHEME_ONLY, VALIDATOR, \ ALIASING_MAP, ALIASING_TERMS_MAP, \ ALL_POSSIBLE_TERMS_SET, SIEM_FIELDS, FIELD_NAME_TO_CLI_NAME SCHEME_ONLY = demisto.args().get('incidentSamplesType') in [SAMPLES_OUTGOING, SAMPLES_SCHEME] fields = demisto.args().get('incidentFields', {}) fields = [x for x in fields if not x.get(INCIDENT_FIELD_UNMAPPED, False)] if fields and len(fields) > 0: fields_names = list(map(lambda x: x['name'], fields)) SIEM_FIELDS = filter_by_dict_by_keys(SIEM_FIELDS, fields_names) for custom_field in filter(lambda x: not x['system'], fields): field_name = custom_field[INCIDENT_FIELD_NAME] SIEM_FIELDS[field_name] = {'aliases': generate_aliases(field_name), 'validators': []} FIELD_NAME_TO_CLI_NAME = {field[INCIDENT_FIELD_NAME]: field[INCIDENT_FIELD_MACHINE_NAME] for field in fields} ALIASING_MAP, ALIASING_TERMS_MAP = get_aliasing(SIEM_FIELDS) terms = [] # type: List[str] for field in SIEM_FIELDS.values(): for alias in field['aliases']: # type: ignore terms += alias.split(" ") ALL_POSSIBLE_TERMS_SET = set(terms) def main(): init() incidents_samples = demisto.args().get('incidentSamples') if incidents_samples: if isinstance(incidents_samples, str): incidents_samples = json.loads(incidents_samples) # type: ignore incidents = list(map(parse_incident_sample, incidents_samples)) else: return_error("Could not parse incident samples") original_mapper = demisto.args().get('currentMapper') if type(original_mapper) is not dict or len(original_mapper) == 0: original_mapper = None matches = match_for_incidents(incidents) if demisto.args().get('incidentSamplesType') == SAMPLES_OUTGOING: mapper = {v: format_incident_field_to_mapper(k, FIELD_NAME_TO_CLI_NAME) for k, v in matches.items() if k in FIELD_NAME_TO_CLI_NAME} else: mapper = {k: format_value_to_mapper(v) for k, v in matches.items()} mapper = combine_mappers(original_mapper, mapper, incidents) return mapper if __name__ in ['__main__', '__builtin__', 'builtins']: demisto.results(main())