class Waybackurls(ScriptCollector): ############### # Plugin attributes ############### _name_ = "waybackurls" _description_ = "Enumerate URLS using the Wayback machine" _author_ = "Louis" _version_ = 1 _dependencies_ = [BinaryDependency("waybackurls")] ############### # Collector attributes ############### _allowed_input_ = Domain _active_scanning_ = False ############### # Script attributes ############### _script_path_ = "waybackurls" _script_arguments_ = ["$Domain.fqdn$"] def parse_result(self, result): uris = result.split("\n") if uris: for uri in uris: yield URI(full_uri=uri)
class Infoga(ScriptCollector): ############### # Plugin attributes ############### _name_ = "Infoga" _description_ = "Find emails from subdomains from public sources. (seach engines, PGP key servers, shodan)" _author_ = "Louis" _version_ = 1 _dependencies_ = [BinaryDependency("infoga")] ############### # Collector attributes ############### _allowed_input_ = Domain _active_scanning_ = False ############### # Script attributes ############### _script_path_ = "infoga" _script_arguments_ = ["--domain", "$Domain.fqdn$", "--breach"] def parse_result(self, result): emails = re.findall(" Email: *(.*?) *\\(*?\\)", result) if emails: for email in emails: yield Email(address=email)
class Wappalyzer(ScriptCollector): ############### # Plugin attributes ############### _name_ = "wappalyzer" _description_ = "Uncovers technologies used on websites" _author_ = "Louis" _version_ = 1 _dependencies_ = [BinaryDependency("wappalyzer")] ############### # Collector attributes ############### _allowed_input_ = URI ############### # Script attributes ############### _script_path_ = "wappalyzer" _script_arguments_ = ["$URI.full_uri$"] def parse_result(self, result): result = json.loads(result) for app in result["applications"]: yield CPE( id=app["cpe"], confidence=app["confidence"], name=app["name"], version=app["version"], website=app["website"], )
class Sublister(ScriptCollector): ############### # Plugin attributes ############### _name_ = "Sublist3r" _description_ = "DNS subdomains enumeration using sublist3r." _author_ = "Louis" _version_ = 1 _dependencies_ = [BinaryDependency("sublist3r")] ############### # Collector attributes ############### _allowed_input_ = Domain ############### # Script attributes ############### _script_path_ = "sublist3r" _script_arguments_ = ["--domain", "$Domain.fqdn$"] def parse_result(self, result): res = result.split("Total Unique Subdomains")[1] if res: domains = res.split("\n") for domain in domains[1:]: yield Domain(fqdn=domain[5:-4])
class Ctfr(ScriptCollector): ############### # Plugin attributes ############### _name_ = "ctfr" _description_ = "Subdomains enumaration using certificate transparency." _author_ = "Louis" _version_ = 1 _dependencies_ = [BinaryDependency("ctfr")] ############### # Collector attributes ############### _allowed_input_ = Domain ############### # Script attributes ############### _script_path_ = "ctfr" _script_arguments_ = ["--domain", "$Domain.fqdn$"] def parse_result(self, result): found_domains = re.findall("\\[-\\] *(.*)", result) if found_domains: for f in found_domains: yield Domain(fqdn=f)
class NmapTCPConnect(ScriptCollector): ############### # Plugin attributes ############### _name_ = "Nmap TCP connect" _description_ = "Performs nmap TCP connect scan (-sT)" _author_ = "Louis" _version_ = 1 _dependencies_ = [BinaryDependency("nmap")] ############### # Collector attributes ############### _allowed_input_ = (Domain, IPv4) _active_scanning_ = True ############### # Script attributes ############### _script_path_ = "nmap" _script_arguments_ = ["-sT", "-oX", "-", "$Domain.fqdn$", "$IPv4.address$"] def parse_result(self, result): found_ports = re.findall( 'protocol="(.+?)" portid="(.+?)"><state state="(.+?)"', result) if not found_ports: return res = [] for port in found_ports: proto, port_number, state = port res.append(Port(number=port_number, state=state, transport=proto)) return res
class Twint(ScriptCollector): ############### # Plugin attributes ############### _name_ = "twint" _description_ = "Gather information from a user's twitter profile." _author_ = "Louis" _version_ = 1 _dependencies_ = [BinaryDependency("twint")] ############### # Collector attributes ############### _allowed_input_ = Username _active_scanning_ = False ############### # Script attributes ############### _script_path_ = "twint" def launch(self, fact): gather_tweets = [self._script_path_, "-u", fact.name.value] gather_favorites = [ self._script_path_, "-u", fact.name.value, "--favorites" ] gather_followers = [ self._script_path_, "-u", fact.name.value, "--followers" ] gather_following = [ self._script_path_, "-u", fact.name.value, "--following" ] yield from self.parse_tweets(self._exec(*gather_tweets)) yield from self.parse_tweets(self._exec(*gather_favorites)) yield from self.parse_usernames(self._exec(*gather_followers)) yield from self.parse_usernames(self._exec(*gather_following)) @staticmethod def parse_tweets(stdout): found_tweets = re.findall( "(\\d{15,21}) ([12]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01]) (?:(?:[01]?\\d|2[0-3]:)?[0-5]?\\d:)?[0-5]?\\d) (.*) <(.*)> (.*)", # noqa: E501 stdout, ) if found_tweets: for f in found_tweets: t_id, date, _, _, tz, username, message = f yield Tweet(id=t_id, message=message, author=username, date=date) @staticmethod def parse_usernames(stdout): followers = stdout.split("\n") if followers: for name in followers: if name and not name.startswith("CRITICAL:"): yield Username(name=name)
class Socialscan(ScriptCollector): ############### # Plugin attributes ############### _name_ = "Socialscan" _description_ = "Find accounts from a given username/email" _author_ = "Louis" _version_ = 1 _dependencies_ = [BinaryDependency("socialscan")] ############### # Collector attributes ############### _active_scanning_ = False _allowed_input_ = (Username, Email) ############### # Script attributes ############### _script_path_ = "socialscan" def launch(self, fact): if isinstance(fact, Username): command = [ self._script_path_, fact.name.value, "-v", "--platforms", "twitter", "github", "tumblr", "lastfm", "snapchat", "gitlab", "reddit", "yahoo", ] elif isinstance(fact, Email): command = [ self._script_path_, fact.address.value, "-v", "--platforms", "twitter", "github", "tumblr", "lastfm", "pinterest", "spotify", ] yield from self.parse_result(self._exec(*command)) def parse_result(self, result): profiles = re.findall("Checked *(.*?) *on *(.*?) *: (?!Available)", result) if profiles: for profile in profiles: username, website = profile yield SocialProfile(username=username, site=website)
class Gitrob(ScriptCollector): ############### # Plugin attributes ############### _name_ = "Gitrob" _description_ = "Find sensitive files from public repositories." _author_ = "Louis" _version_ = 1 _dependencies_ = [BinaryDependency("gitrob"), PasswordDependency("github_token")] ############### # Collector attributes ############### _allowed_input_ = Username ############### # Script attributes ############### _script_path_ = "gitrob" _script_arguments_ = [ "-commit-depth", "50", "-github-access-token", Store().get_decrypted_password("github_token"), "$Username.name$", ] def parse_result(self, result): repos = re.findall( "Path\\.*: (.*)\\r\\n Repo\\.*: (.*)\\r\\n Message\\.*: (.*)\\r\\n Author\\.*: (.*) \\<(.*)\\>\\r\\n( *Comment\\.*: .*\\r\\n)? File URL\\.*: (.*)\\r\\n Commit URL.*: (.*)\\r\\n -*", # noqa: E501 result, ) if repos: for r in repos: path, repo, _, author, email, comment, file_url, commit_url = r raw_file_url = file_url.replace( "/github.com/", "/raw.githubusercontent.com/" ) raw_file_url = raw_file_url.replace("/blob/", "/") raw_file_name = raw_file_url.split("/")[-1] yield GitRepository( url="https://github.com/{}.git".format(repo), host="github.com", username=repo.split("/")[0], project=repo.split("/")[1], ) try: raw_file_ext = raw_file_name.split(".")[-1] except Exception: raw_file_ext = None yield Email(address=email) yield Username(address=author) yield File( filename=raw_file_name, url=raw_file_url, extension=raw_file_ext )
class TheHarvester(ScriptCollector): ############### # Plugin attributes ############### _name_ = "TheHarvester" _description_ = "Gather company domain from public sources." _author_ = "Louis" _version_ = 1 _dependencies_ = [BinaryDependency("theHarvester")] ############### # Collector attributes ############### _allowed_input_ = Domain _active_scanning_ = False ############### # Script attributes ############### _script_path_ = "theHarvester" _script_arguments_ = [ "--domain", "$Domain.fqdn$", "-b", "baidu,bing,certspotter,crtsh,dnsdumpster,dogpile,duckduckgo,google,intelx,linkedin,linkedin_links,netcraft,otx,threatcrowd,trello,twitter,vhost,virustotal,yahoo", ] def parse_result(self, result): found_ips = re.search( "\\[\\*\\] IPs found: \\d*\\n-------------------\\n((.|\\n)*?)\\[\\*\\]", result, ) found_domains = re.search( "\\[\\*\\] Hosts found: \\d*\\n---------------------\\n((.|\\n)*)\\n\\[\\*\\]", result, ) if found_domains and found_domains.group(1): domains_ip = re.findall( "(.*):(\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b)?", found_domains.group(1), ) if domains_ip: for d in domains_ip: domain, ip = d yield Domain(fqdn=domain, ip=ip) if found_ips and found_ips.group(1): ips = re.findall( "(\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b)", found_ips.group(1)) if ips: for i in ips: yield IPv4(address=i)
class Zen(ScriptCollector): ############### # Plugin attributes ############### _name_ = "Zen" _description_ = "Find email addresses of Github users." _author_ = "Louis" _version_ = 1 _dependencies_ = [ BinaryDependency("zen"), PasswordDependency("github_user") ] ############### # Collector attributes ############### _allowed_input_ = (Organization, Username, GitRepository) _active_scanning_ = False ############### # Script attributes ############### _script_path_ = "zen" def launch(self, fact): cmd = [ self._script_path_, "-u", Store().get_decrypted_password("github_user") ] if isinstance(fact, Username): cmd.append(fact.name.value) elif isinstance(fact, GitRepository): url = (fact.url.value[:-4] if fact.url.value.endswith(".git") else fact.url.value) cmd.append(url) elif isinstance(fact, Organization): cmd.extend(["--org", fact.name.value]) yield from self.parse_result(self._exec(*cmd)) @staticmethod def parse_result(result): res = re.findall("(.*) : (.*)", result) if res: for r in res: username, email = r yield Username(name=username) yield Email(address=email)
class NmapOSScan(ScriptCollector): ############### # Plugin attributes ############### _name_ = "Nmap OS scan" _description_ = "Performs nmap OS detection" _author_ = "Louis" _version_ = 1 _dependencies_ = [BinaryDependency("nmap")] ############### # Collector attributes ############### _allowed_input_ = (Domain, IPv4) _active_scanning_ = False ############### # Script attributes ############### _script_path_ = "nmap" _script_arguments_ = [ "-O", "--osscan-guess", "-oX", "-", "$Domain.fqdn$", "$IPv4.address$", ] def parse_result(self, result): found_os = re.findall( '<osclass type="(.+?)" vendor="(.+?)" osfamily="(.+?)" osgen="(.+?)" accuracy="(.+?)">', # NOQA result, ) if not found_os: return res = [] for os in found_os: _, vendor, osfamily, osgen, accuracy = os res.append( OperatingSystem(family=osfamily, version=osgen, vendor=vendor, weight=accuracy)) return res
class NmapNSE(ScriptCollector): ############### # Plugin attributes ############### _name_ = "Nmap nse" _description_ = "Nmap vulnerability scan using vulscan (VulDB)" _author_ = "Louis" _version_ = 1 _dependencies_ = [BinaryDependency("nmap")] ############### # Collector attributes ############### _allowed_input_ = (Domain, IPv4) _active_scanning_ = False ############### # Script attributes ############### _script_path_ = "nmap" _script_arguments_ = [ "--script", "vulscan", # "--script-args", "vulscandb=exploitdb.csv,cve.csv,scipvuldb.csv", "-sV", "-oX", "-", "$Domain.fqdn$", "$IPv4.address$", ] def parse_result(self, result): nse_results = re.findall('<script id="vulscan" output="(.*)/>', result) for i in nse_results: entries_vulscan = re.findall("\\[([\\d]{4,10})] (.*?)
", i) entries_cve = re.findall("\\[CVE-(.*?)\\] (.*?)&", i) if entries_cve: for entry in entries_cve: _id, description = entry yield CVE(id="CVE-{}".format(_id), description=description) if entries_vulscan: for entry in entries_vulscan: _id, description = entry yield VulDB(id=int(_id), description=description)
class WafWoof(ScriptCollector): ############### # Plugin attributes ############### _name_ = "WafW00f" _description_ = "Detect if a website is using a WAF" _author_ = "Louis" _version_ = 1 _dependencies_ = [BinaryDependency("wafw00f")] ############### # Collector attributes ############### _allowed_input_ = (Domain, IPv4) _active_scanning_ = False ############### # Script attributes ############### _script_path_ = "wafw00f" def launch(self, fact): if isinstance(fact, Domain): commands = [ [self._script_path_, "http://{}".format(fact.fqdn.value)], [self._script_path_, "https://{}".format(fact.fqdn.value)], ] elif isinstance(fact, IPv4): commands = [ [self._script_path_, "http://{}".format(fact.address.value)], [self._script_path_, "https://{}".format(fact.address.value)], ] for command in commands: yield from self.parse_result(self._exec(*command)) def parse_result(self, result): found_waf = re.findall("is behind *(.*?) \\(?(.*?)\\)? *WAF.", result) if found_waf: for f in found_waf: try: name, vendor = f yield Waf(name=name, vendor=vendor) except Exception: yield Waf(name=f)
class Kupa3(ScriptCollector): ############### # Plugin attributes ############### _name_ = "Kupa3" _description_ = "Extract javascript files and trackers." _author_ = "Louis" _version_ = 1 _dependencies_ = [BinaryDependency("kupa3")] ############### # Collector attributes ############### _allowed_input_ = Domain ############### # Script attributes ############### _script_path_ = "kupa3" def launch(self, fact): commands = [ [self._script_path_, "http://{}".format(fact.fqdn.value)], [self._script_path_, "https://{}".format(fact.fqdn.value)], ] for command in commands: yield from self.parse_result(self._exec(*command)) def parse_result(self, result): links = re.findall("LINK->(.*)\\n", result) scripts = re.findall("SCRIPT-> (.*)\\n", result) if links: for link in links: yield Domain(fqdn=link) if scripts: for script in scripts: yield File(filename=script.split("/")[-1], url=script, extension="js")
class GobusterDir(ScriptCollector): ############### # Plugin attributes ############### _name_ = "GoBuster dir" _description_ = "Directory brute force using gobuster." _author_ = "Louis" _version_ = 1 _dependencies_ = [ BinaryDependency("gobuster"), FileDependency("/srv/wordlists/subdomains-1000.txt"), ] ############### # Collector attributes ############### _allowed_input_ = Domain ############### # Script attributes ############### _script_path_ = "gobuster" _script_arguments_ = [ "dir", "--url", "$Domain.fqdn$", "--wordlist", "/srv/wordlists/subdomains-1000.txt", "--insecuressl", "--expanded", "--quiet", "--noprogress", "--followredirect", ] def parse_result(self, result): found_domains = re.findall("(.*) \\(Status:", result) if found_domains: for f in found_domains: yield URI(full_uri=f)
class GobusterDNS(ScriptCollector): ############### # Plugin attributes ############### _name_ = "GoBuster DNS" _description_ = "DNS subdomains brute force using gobuster." _author_ = "Louis" _version_ = 1 _dependencies_ = [ BinaryDependency("gobuster"), FileDependency("/srv/wordlists/directories-big.txt"), ] ############### # Collector attributes ############### _allowed_input_ = Domain ############### # Script attributes ############### _script_path_ = "gobuster" _script_arguments_ = [ "dns", "--domain", "$Domain.fqdn$", "--wordlist", "/srv/wordlists/directories-big.txt", "--quiet", "--noprogress", "--showips", ] def parse_result(self, result): found_domains = re.findall("Found: (.*) \\[(.*)\\]\\n", result) if found_domains: for f in found_domains: domain, ip = f yield Domain(fqdn=domain, ip=ip)
class Profiler(ScriptCollector): ############### # Plugin attributes ############### _name_ = "Profiler" _description_ = "OSINT HUMINT Profile Collector" _author_ = "Louis" _version_ = 1 _dependencies_ = [BinaryDependency("recon-ng")] ############### # Collector attributes ############### _active_scanning_ = False _allowed_input_ = Username ############### # Script attributes ############### _script_path_ = "recon-ng" _script_arguments_ = [ "-m", "recon/profiles-profiles/profiler", "-o", "SOURCE=$Username.name$", "-x", ] def parse_result(self, result): results = [] found_social_profiles = re.findall( "(.*)\\[profile\\] (.*) - (.*) \\((.*)\\)", result) if not found_social_profiles: return results for f in found_social_profiles: _, username, site, url = f results.append(SocialProfile(username=username, url=url, site=site)) return results
class Sherlock(ScriptCollector): ############### # Plugin attributes ############### _name_ = "Sherlock" _description_ = "Find usernames across social networks" _author_ = "Louis" _version_ = 1 _dependencies_ = [BinaryDependency("sherlock")] ############### # Collector attributes ############### _active_scanning_ = False _allowed_input_ = Username ############### # Script attributes ############### _script_path_ = "sherlock" def launch(self, fact): cmd = [ self._script_path_, fact.name.value, "--print-found", "--folderoutput", "/tmp", ] yield from self.parse_result(self._exec(*cmd), fact.name.value) def parse_result(self, result, username): found_social_profiles = re.findall("\\[\\+\\] (.*): (.*)", result) if found_social_profiles: for f in found_social_profiles: site, url = f yield SocialProfile(username=username, site=site, url=url)
class HackerTarget(ScriptCollector): ############### # Plugin attributes ############### _name_ = "Hacker target" _description_ = "Uses the HackerTarget.com API to find host names." _author_ = "Louis" _version_ = 1 _dependencies_ = [BinaryDependency("recon-ng")] ############### # Collector attributes ############### _allowed_input_ = Domain ############### # Script attributes ############### _script_path_ = "recon-ng" _script_arguments_ = [ "-m", "recon/domains-hosts/hackertarget", "-o", "SOURCE=$Domain.fqdn$", "-x", ] def parse_result(self, result): results = [] found_domains = re.findall("(.*)\\[host\\] (.*) \\((.*)\\)", result) if not found_domains: return results for f in found_domains: _, domain, url = f results.append(Domain(fqdn=domain)) return results
class TruffleHog(ScriptCollector): ############### # Plugin attributes ############### _name_ = "Truffle Hog" _description_ = "Find keys and secrets from public repositories." _author_ = "Henry" _version_ = 1 _dependencies_ = [BinaryDependency("trufflehog")] ############### # Collector attributes ############### _allowed_input_ = GitRepository ############### # Script attributes ############### _script_path_ = "trufflehog" _script_arguments_ = [ "$GitRepository.url$", "--rules", os.path.dirname(__file__) + "/config/rulesRegex.json", "--regex", "--entropy=False", "--json", ] def launch(self, fact): if not is_list(fact): fact = [fact] args = self._find_and_replace_sigil(fact) stdout = self._exec(self.script_path, *args, ignore_error=True) return self.parse_result(stdout) def clear_secret(self, res, regex): for key in res["stringsFound"]: extracted = re.findall(regex, key) yield extracted[0].replace("'", "").replace('"', "") def parse_secret_found(self, res): if res["reason"] == "Generic_Secret" or res[ "reason"] == "Generic_API_Key": return list( self.clear_secret(res, r"['|\"][0-9a-zA-Z]{32,45}['|\"]")) if res["reason"] == "Twitter_OAuth" or res[ "reason"] == "Facebook_OAuth": return list(self.clear_secret(res, r"[\"\'][^ ]*[\"\']")) return res["stringsFound"] def duplicate(self, res, val, Final_Result): for final in Final_Result: if final.categorie.value == res["reason"]: if final._name_ == "Secret" and final.secret.value == val: return True elif final._name_ == "CryptoKey" and final.key.value == val: return True def Fact_selection(self, res, value): if value[:10] == "-----BEGIN": _categorie = re.findall(r"BEGIN (.*) PRIVATE", value)[0] return CryptoKey(key=value, categorie=_categorie) return Secret(secret=value, categorie=res["reason"]) def parse_result(self, result): result_json = json.loads("[" + ",".join(result.split("\n")) + "]") Final_Result = [] for res in result_json: res["stringsFound"] = self.parse_secret_found(res) for val in res["stringsFound"]: if len(val) > 4096 or self.duplicate(res, val, Final_Result): continue Final_Result.append(self.Fact_selection(res, val)) return Final_Result
class BlackWidow(ScriptCollector): ############### # Plugin attributes ############### _name_ = "BlackWidow" _description_ = ( "Gather URLS, dynamic parameters and email addresses from a target website." ) _author_ = "Louis" _version_ = 1 _dependencies_ = [BinaryDependency("blackwidow")] ############### # Collector attributes ############### _allowed_input_ = Domain ############### # Script attributes ############### _script_path_ = "blackwidow" _script_arguments_ = ["-d", "$Domain.fqdn$"] @staticmethod def read_file_line_by_line(filepath): with open(filepath) as fp: line = fp.readline() while line: yield line.strip() line = fp.readline() def return_uri(self, filepath): if filepath and filepath.group(1): for line in self.read_file_line_by_line("{}.txt".format(filepath.group(1))): yield URI(full_uri=line) def parse_result(self, result): urls_file = re.search("\\[\\+\\] URL's Discovered:.*\\n(.*).txt", result) dynamic_urls_file = re.search( "\\[\\+\\] Dynamic URL's Discovered:.*\\n(.*).txt", result ) form_urls_file = re.search( "\\[\\+\\] Form URL's Discovered:.*\\n(.*).txt", result ) dynamic_parameters_file = re.search( "\\[\\+\\] Unique Dynamic Parameters Discovered:.*\\n(.*).txt", result ) sub_domains_file = re.search( "\\[\\+\\] Sub-domains Discovered:.*\\n(.*).txt", result ) emails_file = re.search("\\[\\+\\] Emails Discovered:.*\\n(.*).txt", result) phones_file = re.search("\\[\\+\\] Phones Discovered:.*\\n(.*).txt", result) uris = [urls_file, dynamic_urls_file, form_urls_file, dynamic_parameters_file] for uri_path in uris: yield from self.return_uri(uri_path) if sub_domains_file and sub_domains_file.group(1): for line in self.read_file_line_by_line( "{}.txt".format(sub_domains_file.group(1)) ): yield Domain(address=line) if emails_file and emails_file.group(1): for line in self.read_file_line_by_line( "{}.txt".format(emails_file.group(1)) ): yield Email(address=line) if phones_file and phones_file.group(1): for line in self.read_file_line_by_line( "{}.txt".format(phones_file.group(1)) ): yield Phone(number=line)
class PhoneInfoga(ScriptCollector): ############### # Plugin attributes ############### _name_ = "PhoneInfoga" _description_ = "Gather information from international phone numbers." _author_ = "Louis" _version_ = 1 _dependencies_ = [BinaryDependency("phoneinfoga")] ############### # Collector attributes ############### _allowed_input_ = Phone _active_scanning_ = False ############### # Script attributes ############### _script_path_ = "phoneinfoga" def launch(self, fact): cmd_ovh = [ self._script_path_, "--no-ansi", "--scanner", "ovh", "--number", fact.number.value, ] cmd_numverify = [ self._script_path_, "--no-ansi", "--scanner", "numverify", "--number", fact.number.value, ] exec_numverify = self._exec(*cmd_numverify) yield from self.parse_ovh_result(self._exec(*cmd_ovh), fact.number.value) yield from self.parse_numverify_result(exec_numverify, fact.number.value) yield from self.parse_local_result(exec_numverify) def parse_local_result(self, result): res = re.findall( "Running local scan...\\n\\[\\+\\] International format: (.*)\\n\\[\\+\\] Local format: (.*)\\n\\[\\+\\] Country found: (.*) \\((.*)\\)\\n\\[\\+\\] City\\/Area: (.*)\\n\\[\\+\\] Carrier: (.*)\\n\\[\\+\\] Timezone: (.*)\\n", # noqa: E501 result, ) if res: for info in res: il_format, local_f, country, cc, area, carrier, tz = info yield Country(name=country, timezone=tz, area=area) yield Phone( number=il_format, localformat=local_f, carrier=carrier, country_code=cc, ) def parse_numverify_result(self, result, number): res = re.findall( "Running Numverify.com scan...\\n\\[\\+\\] Number: \\((.*)\\) (.*)\\n\\[\\+\\] Country: (.*) \\((.*)\\)\n\\[\\+\\] Location: (.*)\\n\\[\\+\\] Carrier: (.*)\\n\\[\\+\\] Line type: (.*)\\n", # noqa: E501 result, ) if res: for info in res: cc, ln, country, cs, _, carrier, lt = info yield Country(name=country, code=cs) yield Phone( number=number, localformat=ln, carrier=carrier, country_code=cc, line_type=lt, ) def parse_ovh_result(self, result, number): yield # TODO