import re from urllib.parse import parse_qs, unquote, urlparse import demistomock as demisto from CommonServerPython import * from tld import get_fld from validate_email import validate_email # ============================================================================================================== # # This script is highly similar to 'ExtractFQDNFromUrlAndEmail', they are not unified due to run time performance. # Please change both scripts respectively. # ============================================================================================================== # PROOFPOINT_PREFIXES = ["https://urldefense.proofpoint.com/v1/url?u=", "https://urldefense.proofpoint.com/v2/url?u="] ATP_LINK_REG = r"(https:\/\/\w*|\w*)\.safelinks\.protection\.outlook\.com\/.*\?url=" def atp_get_original_url(safe_url): split_url = urlparse(safe_url) query = split_url.query query_dict = parse_qs(query) encoded_url_list = query_dict.get("url", []) encoded_url = encoded_url_list[0] if len(encoded_url_list) >= 1 else None if not encoded_url: error_msg = "Could not decode ATP Safe Link. Returning original URL." demisto.info(error_msg) return safe_url decoded_url = unquote(encoded_url) return decoded_url def proofpoint_get_original_url(safe_url): regex = r"&.*$" split_url = urlparse(safe_url) query = split_url.query query_dict = parse_qs(query) encoded_url_list = query_dict.get("u", []) encoded_url = encoded_url_list[0] if len(encoded_url_list) >= 1 else None clean = encoded_url.replace("-", "%").replace("_", "/").replace(regex, "") if encoded_url else None clean = unquote(clean) if clean else None return clean def unescape_url(escaped_url): # Normalize: 1) [.] --> . 2) hxxp --> http 3) & --> & 4) http:\\ --> http:// url = escaped_url.lower().replace("[.]", ".").replace("hxxp", "http").replace("&", "&").replace("http:\\\\", "http://") # Normalize the URL with http prefix if url.find("http:") == 0 and url.find("http://") == -1: url = url.replace("http:", "http://") if url.find("http") != 0 and url.find("ftp") != 0: return "http://" + url return url def extract_domain(the_input): is_url = None domain_from_mail = None is_email = validate_email(the_input) if is_email: # Take the entire part after the @ of the email domain_from_mail = the_input.split("@")[1] else: # Test if URL, else proceed as domain # Check if it is a Microsoft ATP Safe Link if re.match(ATP_LINK_REG, the_input): the_input = atp_get_original_url(the_input) # Check if it is a Proofpoint URL elif the_input.find(PROOFPOINT_PREFIXES[0]) == 0 or the_input.find(PROOFPOINT_PREFIXES[1]) == 0: the_input = proofpoint_get_original_url(the_input) # Not ATP Link or Proofpoint URL so just unescape else: the_input = unescape_url(the_input) is_url = domain = get_fld(the_input, fail_silently=True) # Extract domain itself from a potential subdomain if domain_from_mail or not is_url: full_domain = "https://" full_domain += domain_from_mail if domain_from_mail else the_input # get_tld fails to parse subdomain since it is not URL, over-ride error by injecting protocol. domain = get_fld(full_domain, fail_silently=True) # convert None to empty string if needed domain = domain if domain else "" return domain def main(): domains = [] the_input = demisto.args().get("input") # argToList returns the argument as is if it's already a list so no need to check here the_input = argToList(the_input) # Otherwise assumes it's already an array for item in the_input: domains.append(extract_domain(item)) demisto.results(domains) # python2 uses __builtin__ python3 uses builtins if __name__ == "__builtin__" or __name__ == "builtins": main()