import os, pwd def main(): rootpath='/srv/darknet-onion-webring/' urlpath=pwd.getpwuid(os.getuid()).pw_dir+"/.darknet_participant_url" print(urlpath) isitvalid="n" #check if ~/.darknet_participant_url exists, # if exists, instance= the content of ~/.darknet_participant_url (which is the url: such as uptime.nowherejez...onion) while isitvalid != "y": if os.path.isfile(urlpath): with open(urlpath) as f: instance = f.read() # TODO check if the instance URL domain is valid instancepath=rootpath+'www/participants/'+instance print("[+] file exists, your Webring URL is ", instance) isitvalid = "y" else: print("[+] Instance Path doesn't exist yet") # and ask for the instance URL domain instance = input("What is your Instance domain ? (ex: uptime.nowherejezfoltodf4jiyl6r56jnzintap5vyjlia7fkirfsnfizflqd.onion):") instancepath=rootpath+'www/participants/'+instance # TODO check if the instance URL domain is valid # ask the user if the instance URL is valid ? print() print(instance) isitvalid=input("Is your this your instance domain ?") # if yes, then write it into ~/.darknet_participant_url if isitvalid != "y" : print("OK writing the instance url to ~/.darknet_participants_url") with open(urlpath, "w") as file: file.write(instance) print("[+] file written, let's read it") f = open(urlpath,"r") print(f.read()) print("[+] Initial Setup Completed!") print("[+] Welcome to the Darknet Onion Webring, where you are exploring the Darknet and helping others do the same.") print(""" 1) Add a new entry (into unverified.csv) 2) Verify an entry (move an entry from unverified to verified.csv) 3) Add a new webring participant (and download their files into their directory (without trusting them yet!)) 4) Trust a webring participant (Potentially dangerous) 5) Add/Remove words in the sensitive list (ex: drug) 6) Add/Remove words in the blacklist (ex: porn) """) option = input("Select Option? (1-4)") print(option) match option: case "1": print("[+] Add a new Entry (into unverified.csv)") # ask for the following: unverifiedpath=instancepath+'/unverified.csv' # the name of the website (required) + check if its valid entry_name = input("What is the Website name ?") # the url of the website (required) + check if its valid entry_url = input("What is URL of the Website ? (ex: https://torproject.org or http://2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion") # a quick description (optional) + check if its valid entry_url = input("(Optional) Description of the website ? (max 256 characters) (press enter to skip)" # sensitive ? (y/n) + check if its valid entry_url = input("is it a sensitive website ? (ex: website related to drugs)" # (rest is automatic: status, score, instance is = '' because it is your own instance) # check if the entry doesn't already exist in verified.csv and in unverified.csv # if it doesnt exist, add it into unverified.csv case "2": print("[+] Verify an entry (move an entry from unverified.csv to verified.csv)") # search for a word searchterm = input("What is the Website name ?") # and display only the matching entries in an array format (display it in CLI). # Each of the rows must have an index, # prompt the user to ask for with row they want to move to verified.csv searchterm = input("What is the index of the entry that you want to move to verified.csv ?") # once selected, it must be able to print that row, and: # append it into verified.csv # remove it from unverified.csv # print("[+] completed! You are now listing [name] (url) (sensitive)") case "3": print("[+] Add a new webring participant (and download their files into their directory (without trusting them yet!))") # ask for the url to the other webring participant webring_participant_url = input("What is URL of the new webring participant? (ex: https://torproject.org or http://2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion") # check if the (onion only) url is valid or not: at http://URL.onion/participants/URL.onion/{verified.csv,unverified.csv,sensitive.csv,blacklist.csv,webring-participants.csv} case "4": print("[+] Trust a webring participant (Potentially dangerous)") # list the existing webring participants from webring-participants.csv and # ask the user to pick the index (0-9?) of the instance to trust # if index is valid, then mark the instance as trusted in webring-participants.csv searchterm = input("What is the index of the entry that you want to trust ?") case "5": print("[+] Add/Remove words in the sensitive list (ex: drug)") print("do you want to 1) add words or 2) remove words ?") option="0" done = False while(done == False): while option not any((1,2,"exit")): option=input("do you want to 1) add words or 2) remove words ? (type exit to exit)") # display the contents of sensitive.csv file if option == 1: word=input("which word do you want to add? (write 0 to exit") if word == "0": done = True #True to get out of the while loop else print("checking if word is valid") #check if word is valid in a while loop (dont check if # if invalid! remove word at index else: index=input("which word do you want to remove? (index 0 to (max index) (write exit to exit)") if index == "exit": done = True #True to get out of the while loop else print("checking if index is valid") #check if index is valid or not # if valid! remove word at index # if invalid! just pass to ask for another word case "6": print("[+] Add/Remove words in the blacklist list (ex: porn)") # copy option 5 # print("do you want to 1) add words or 2) remove words ?") # display the contents of blacklist.csv file case _: print("[-] ERROR, incorrect input") #### PROTECTIONS AGAINST MALICIOUS CSV INPUTS #### 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: 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:/.]+$") if url.endswith('.onion'): return IsOnionValid(url) else: if not url.__contains__('.'): print("No (DOT) in 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,w]. Verbose only if False is returned """ # check if the characters are only [vx] with maximum 1 chars max # if OK return True #if not : return False pattern = ['v','x'] if len(status) != 1: print("Got more than one character or nothing.") return False elif (status not in pattern): print("Got an invalid character") return False 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 pattern = re.compile("^[A-Za-z0-9-.,' ]+$") desc.strip() if pattern.fullmatch(desc) is None: print('Got an empty desc or invalid chars') 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') 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 lenght greater than 64. {len(name)}') return False return True if __name__ == '__main__': main()