import os, pwd, re, pandas as pd, requests, shutil from PIL import Image import urllib import socks, socket, glob PURPLE = '\033[35;40m' BOLD_PURPLE = '\033[35;40;1m' ORANGE = '\033[33;40;1m' RED = '\033[31;40m' BOLD_RED = '\033[31;40;1m' RESET = '\033[m' #### Checking Functions to validate that links are legit #### def CheckUrl(url): """ Checks if URL is actually reachable via Tor """ proxies = { 'http': 'socks5h://127.0.0.1:9050', 'https': 'socks5h://127.0.0.1:9050' } try: status = requests.get(url,proxies=proxies, timeout=5).status_code #print('[+]',url,status) if status != 502: #print(url,"✔️") return True else: #print(url,"❌") return False except requests.ConnectionError as e: #print(url,"❌") return False except requests.exceptions.ReadTimeout as e: #print(url,"❌") return False #### PROTECTIONS AGAINST MALICIOUS CSV INPUTS #### def IsBannerValid(path: str) -> bool: """ Checks if the banner.png file has the correct dimensions (240x60) """ #print('[+] checking image size') try: im = Image.open(path) except Exception as e: return False #im = Image.open("favicon.png") width, height = im.size #print('width =',width, 'height=',height) if width != 240 or height != 60: #print('[-] Banner doesnt have the correct size (240x60)') return False else: #print('[+] Banner has the correct size (240x60)') return True def IsOnionValid(url: str)-> bool: """ Checks if the domain(param) is a valid onion domain and return True else False. """ # check if the characters are only [a-zA-Z0-9.] with maximum 128 chars max? # check that it is only url.onion or subdomain.url.onion, # if OK return True #if not : return False try: pattern = re.compile("^[A-Za-z0-9.]+(\.onion)?$") url = url.strip().removesuffix('/') if url.startswith('http://'): #print('URL starts with http') # Removes the http:// domain = url.split('/')[2] if pattern.fullmatch(domain) is not None: if len(domain.split('.')) > 3: n_subdomians = len(domain.split('.')) # Checks if there is more than 1 subdomain. "subdomain.url.onion" only #print(f"This domain have more than one subdomain. There are {n_subdomians} subdomains") return False else: if len(domain) < 62: #print("Domain length is less than 62.") return False return True elif pattern.fullmatch(domain) is None: #print("Domain contains invalid character.") #print(domain) return False else: #print("Domain not valid") return False else: #TODO : edit the url to make sure it has http:// at the beginning, in case if it's missing? (problem is that it only returns true or false) #print("URL doesn't start http") if pattern.fullmatch(url) is not None: if len(url.split('.')) > 3: n_subdomians = len(url.split('.')) # Checks if there is more than 1 subdomain. "subdomain.url.onion" only #print(f"This domain have more than one subdomain. There are {n_subdomians - 1} subdomains") return False else: if len(url) < 62: #print("Domain length is less than 62.") return False return True elif pattern.fullmatch(url) is None: #print("Domain contains invalid character.") #print(url) return False else: #print("Domain not valid") return False except Exception as e: print(f"Error: {e}") def IsUrlValid(url:str)->bool: """ Check if url is valid both dark net end clearnet. """ # check if the characters are only [a-zA-Z0-9.:/] with maximum 128 chars max? # check that it is only http(s)://wordA.wordB or http(s)://WordC.WordB.WordC, (onion or not), clearnet is fine too (double check if those are fine!) # if OK return True #if not : return False pattern = re.compile("^[A-Za-z0-9:/.-]+$") url = str(url) if len(url) < 4: #print("Status: Got more than one character or nothing.") return False if url.endswith('.onion'): return IsOnionValid(url) else: if not url.__contains__('.'): #print("No (DOT) in clearnet url") return False if pattern.fullmatch(url) is None: #print('Url contains invalid chars') return False return True def IsStatusValid(status: str)-> bool: """ Checks if status contains only [v,x,❌,✔️]. Verbose only if False is returned """ pattern = ['y','n','✔️','❌','','nan'] status = str(status) status.strip() #print('[+] STATUS = ',status.splitlines()) if len(status) > 4: #print("Status: Got more than one character or nothing.") return False elif (status not in pattern): #print("Status: Got an invalid character it must be either y, n, ✔️, or ❌ ") return False return True def IsScoreValid(score:str)->bool: """ Check the Score is only "^[0-9.,]+$" with 8 max chars. """ # check if the characters are only [a-zA-Z0-9.,' ] with maximum 256 chars max #(careful with the ' and , make sure you test if it fucks the csv up or else) # if OK return True #if not : return False pattern = re.compile("^[0-9.,]+$") score = str(score) score.strip() #pattern = ['','nan'] if score in ['','nan']: #Score can be empty when initially added return True if pattern.fullmatch(score) is None: # empty description is fine as it's optional return False elif len(score) > 8: #print("score is greater than 8 chars") return False # empty score is fine return True def IsDescriptionValid(desc:str)->bool: """ Check the categories are only [a-zA-Z0-9.' ] with 256 max chars. """ # check if the characters are only [a-zA-Z0-9.,' ] with maximum 256 chars max #(careful with the ' and , make sure you test if it fucks the csv up or else) # if OK return True #if not : return False if desc == "": # empty description is fine as it's optional return True pattern = re.compile("^[A-Za-z0-9-.,' \"]+$") desc = str(desc) desc.strip() if pattern.fullmatch(desc) is None: return False if desc == "DEFAULT": return False elif len(desc) > 256: #print("desc is greater than 256 chars") return False return True def IsCategoryValid(categories: list)-> bool: """ Check the categories are only [a-zA-Z0-9 ] with 64 max chars. """ # check if the characters are only [a-zA-Z0-9 ] with maximum 64 chars max #(careful with the ' and , make sure you test if it fucks the csv up or else) # if OK return True #if not : return False pattern = re.compile("^[A-Za-z0-9 ]+$") for category in categories: category.strip() if pattern.fullmatch(category) is None: #print('Got an empty list or invalid chars') return False elif len(category) > 64: #print('Category is too long') return False else: return True def IsNameValid(name: str)->bool: """ Check the parameter name only contains [a-zA-Z0-9 ] and is 64 chars long. """ # check if the characters are only [a-zA-Z0-9 ] with maximum 64 chars max #(careful with the ' and , make sure you test if it fucks the csv up or else) # if OK return True #if not : return False pattern = re.compile("^[A-Za-z0-9 ]+$") name = name.strip() if (pattern.fullmatch(name) is None): #print("Got an invalid character or nothing") return False elif len(name) > 64: #print(f'Got a name length greater than 64. {len(name)}') return False return True #def print_colors(s:str, bold=False, is_error = False, default=False): def print_colors(*args, bold=False, is_error=False, default=False, highlight=False): """ Helper function to print with colors """ for s in args: if is_error: print(f"{RED}{s}{RESET}",end='') elif highlight: print(f"{ORANGE}{s}{RESET}",end='') elif bold: print(f"{BOLD_PURPLE}{s}{RESET}",end='') elif is_error and bold: print(f"{BOLD_RED}{s}{RESET}",end='') elif default: print(f'{s}',end='') else: print(f"{PURPLE}{s}{RESET}",end='') if s is args[-1]: print()