From 2a1ab1c281fbe9abb3bc0593ffc0e1414ebec1db Mon Sep 17 00:00:00 2001 From: "Christopher K. Hoadley" Date: Fri, 27 Dec 2019 10:17:10 -0600 Subject: [PATCH] Add result module to hold results of site queries. The QueryResult() object contains an enumeration for the possible status about a given username on a site, and additional error information that might be handy. Rework all code to use this object instead of the "exists" key in the result dictionary that was used previously. --- sherlock/result.py | 74 ++++++++++++++++++++++++++++++++++++++++++ sherlock/sherlock.py | 46 ++++++++++++-------------- sherlock/tests/base.py | 9 +++-- 3 files changed, 100 insertions(+), 29 deletions(-) create mode 100644 sherlock/result.py diff --git a/sherlock/result.py b/sherlock/result.py new file mode 100644 index 00000000..acbdabd9 --- /dev/null +++ b/sherlock/result.py @@ -0,0 +1,74 @@ +"""Sherlock Result Module + +This module defines various objects for recording the results of queries. +""" +from enum import Enum + + +class QueryStatus(Enum): + """Query Status Enumeration. + + Describes status of query about a given username. + """ + CLAIMED = "Claimed" #Username Detected + AVAILABLE = "Available" #Username Not Detected + UNKNOWN = "Unknown" #Error Occurred While Trying To Detect Username + ILLEGAL = "Illegal" #Username Not Allowable For This Site + + def __str__(self): + """Convert Object To String. + + Keyword Arguments: + self -- This object. + + Return Value: + Nicely formatted string to get information about this object. + """ + return self.value + +class QueryResult(): + """Query Result Object. + + Describes result of query about a given username. + """ + def __init__(self, status, context=None): + """Create Query Result Object. + + Contains information about a specific method of detecting usernames on + a given type of web sites. + + Keyword Arguments: + self -- This object. + status -- Enumeration of type QueryStatus() indicating + the status of the query. + context -- String indicating any additional context + about the query. For example, if there was + an error, this might indicate the type of + error that occurred. + Default of None. + + Return Value: + Nothing. + """ + + self.status = status + self.context = context + + return + + def __str__(self): + """Convert Object To String. + + Keyword Arguments: + self -- This object. + + Return Value: + Nicely formatted string to get information about this object. + """ + status = str(self.status) + if self.context is not None: + #There is extra context information available about the results. + #Append it to the normal response text. + status += f" ({self.context})" + + return status diff --git a/sherlock/sherlock.py b/sherlock/sherlock.py index 03785d18..23781647 100644 --- a/sherlock/sherlock.py +++ b/sherlock/sherlock.py @@ -23,6 +23,8 @@ from colorama import Fore, Style, init from requests_futures.sessions import FuturesSession from torrequest import TorRequest +from result import QueryStatus +from result import QueryResult module_name = "Sherlock: Find Usernames Across Social Networks" __version__ = "0.10.0" @@ -206,7 +208,8 @@ def sherlock(username, site_data, verbose=False, tor=False, unique_tor=False, the following keys: url_main: URL of main site. url_user: URL of user on site (if account exists). - exists: String indicating results of test for account existence. + status: QueryResult() object indicating results of test for + account existence. http_status: HTTP status code of query which checked for existence on site. response_text: Text that came back from request. May be None if @@ -265,7 +268,7 @@ def sherlock(username, site_data, verbose=False, tor=False, unique_tor=False, if not print_found_only: print_invalid(social_network, "Illegal Username Format For This Site!", color) - results_site["exists"] = "illegal" + results_site['status'] = QueryResult(QueryStatus.ILLEGAL) results_site["url_user"] = "" results_site['http_status'] = "" results_site['response_text'] = "" @@ -332,18 +335,14 @@ def sherlock(username, site_data, verbose=False, tor=False, unique_tor=False, # Retrieve other site information again url = results_site.get("url_user") - exists = results_site.get("exists") - if exists is not None: + status = results_site.get("status") + if status is not None: # We have already determined the user doesn't exist here continue # Get the expected error type error_type = net_info["errorType"] - # Default data in case there are any failures in doing a request. - http_status = "?" - response_text = "" - # Retrieve future and ensure it has finished future = net_info["request_future"] r, error_text, expection_text = get_response(request_future=future, @@ -362,35 +361,35 @@ def sherlock(username, site_data, verbose=False, tor=False, unique_tor=False, try: http_status = r.status_code except: - pass + http_status = "?" try: response_text = r.text.encode(r.encoding) except: - pass + response_text = "" if error_text is not None: print_error(social_network, expection_text, error_text, "", verbose, color) - exists = "error" + result = QueryResult(QueryStatus.UNKNOWN, error_text) elif error_type == "message": error = net_info.get("errorMsg") # Checks if the error message is in the HTML if not error in r.text: print_found(social_network, url, response_time, verbose, color) - exists = "yes" + result = QueryResult(QueryStatus.CLAIMED) else: if not print_found_only: print_not_found(social_network, response_time, verbose, color) - exists = "no" + result = QueryResult(QueryStatus.AVAILABLE) elif error_type == "status_code": # Checks if the status code of the response is 2XX if not r.status_code >= 300 or r.status_code < 200: print_found(social_network, url, response_time, verbose, color) - exists = "yes" + result = QueryResult(QueryStatus.CLAIMED) else: if not print_found_only: print_not_found(social_network, response_time, verbose, color) - exists = "no" + result = QueryResult(QueryStatus.AVAILABLE) elif error_type == "response_url": # For this detection method, we have turned off the redirect. @@ -401,19 +400,14 @@ def sherlock(username, site_data, verbose=False, tor=False, unique_tor=False, if 200 <= r.status_code < 300: # print_found(social_network, url, response_time, verbose, color) - exists = "yes" + result = QueryResult(QueryStatus.CLAIMED) else: if not print_found_only: print_not_found(social_network, response_time, verbose, color) - exists = "no" - - elif error_type == "": - if not print_found_only: - print_invalid(social_network, "Error!", color) - exists = "error" + result = QueryResult(QueryStatus.AVAILABLE) - # Save exists flag - results_site['exists'] = exists + # Save status of request + results_site['status'] = result # Save results from request results_site['http_status'] = http_status @@ -642,7 +636,7 @@ def main(): exists_counter = 0 for website_name in results: dictionary = results[website_name] - if dictionary.get("exists") == "yes": + if dictionary.get("status").status == QueryStatus.CLAIMED: exists_counter += 1 file.write(dictionary["url_user"] + "\n") file.write(f"Total Websites Username Detected On : {exists_counter}") @@ -665,7 +659,7 @@ def main(): site, results[site]['url_main'], results[site]['url_user'], - results[site]['exists'], + str(results[site]['status'].status), results[site]['http_status'], results[site]['response_time_s'] ] diff --git a/sherlock/tests/base.py b/sherlock/tests/base.py index 0c557664..62e69c60 100644 --- a/sherlock/tests/base.py +++ b/sherlock/tests/base.py @@ -7,6 +7,8 @@ import os import os.path import unittest import sherlock +from result import QueryStatus +from result import QueryResult import warnings @@ -95,10 +97,10 @@ class SherlockBaseTest(unittest.TestCase): if exist_check: check_type_text = "exists" - exist_result_desired = "yes" + exist_result_desired = QueryStatus.CLAIMED else: check_type_text = "does not exist" - exist_result_desired = "no" + exist_result_desired = QueryStatus.AVAILABLE for username in username_list: results = sherlock.sherlock(username, @@ -112,7 +114,8 @@ class SherlockBaseTest(unittest.TestCase): with self.subTest(f"Checking Username '{username}' " f"{check_type_text} on Site '{site}'" ): - self.assertEqual(result['exists'], exist_result_desired) + self.assertEqual(result['status'].status, + exist_result_desired) return