diff --git a/sherlock_project/notify.py b/sherlock_project/notify.py index f6c785d63f..46e844ecb4 100644 --- a/sherlock_project/notify.py +++ b/sherlock_project/notify.py @@ -5,10 +5,12 @@ """ from sherlock_project.result import QueryStatus from colorama import Fore, Style +import threading import webbrowser -# Global variable to count the number of results. -globvar = 0 +# Thread-safe counter for found results. +_results_lock = threading.Lock() +_results_count = 0 class QueryNotify: @@ -166,18 +168,15 @@ def start(self, message): return def countResults(self): - """This function counts the number of results. Every time the function is called, - the number of results is increasing. + """Increment and return the thread-safe found-results counter.""" + global _results_count + with _results_lock: + _results_count += 1 + return _results_count - Keyword Arguments: - self -- This object. - - Return Value: - The number of results by the time we call the function. - """ - global globvar - globvar += 1 - return globvar + def getResults(self): + """Return the current found-results count without incrementing.""" + return _results_count def update(self, result): """Notify Update. @@ -265,7 +264,7 @@ def finish(self, message="The processing has been finished."): Return Value: Nothing. """ - NumberOfResults = self.countResults() - 1 + NumberOfResults = self.getResults() print(Style.BRIGHT + Fore.GREEN + "[" + Fore.YELLOW + "*" + diff --git a/sherlock_project/sherlock.py b/sherlock_project/sherlock.py index f78d4b8cac..0fad73c375 100644 --- a/sherlock_project/sherlock.py +++ b/sherlock_project/sherlock.py @@ -287,8 +287,11 @@ def sherlock( # from where the user profile normally can be found. url_probe = interpolate_string(url_probe, username) + # Normalise errorType to a list for consistent checks below + _error_type_norm = net_info["errorType"] if isinstance(net_info["errorType"], list) else [net_info["errorType"]] + if request is None: - if net_info["errorType"] == "status_code": + if "status_code" in _error_type_norm: # In most cases when we are detecting by status code, # it is not necessary to get the entire body: we can # detect fine with just the HEAD response. @@ -299,7 +302,7 @@ def sherlock( # not respond properly unless we request the whole page. request = session.get - if net_info["errorType"] == "response_url": + if "response_url" in _error_type_norm: # Site forwards request to a different URL if username not # found. Disallow the redirect so we can capture the # http status from the original URL request. @@ -370,7 +373,7 @@ def sherlock( except Exception: http_status = "?" try: - response_text = r.text.encode(r.encoding or "UTF-8") + response_text = r.text except Exception: response_text = "" @@ -392,7 +395,7 @@ def sherlock( if error_text is not None: error_context = error_text - elif any(hitMsg in r.text for hitMsg in WAFHitMsgs): + elif r is not None and any(hitMsg in r.text for hitMsg in WAFHitMsgs): query_status = QueryStatus.WAF else: @@ -631,11 +634,11 @@ def main(): help="Output sites where the username was not found.", ) parser.add_argument( - "--print-found", - action="store_true", + "--no-print-found", + action="store_false", dest="print_found", default=True, - help="Output sites where the username was found (also if exported as file).", + help="Suppress output of sites where the username was found.", ) parser.add_argument( "--no-color", @@ -712,7 +715,8 @@ def main(): latest_release_json = json_loads(latest_release_raw) latest_remote_tag = latest_release_json["tag_name"] - if latest_remote_tag[1:] != __version__: + remote_version = latest_remote_tag.lstrip("v").lstrip("release-") + if remote_version != __version__: print( f"Update available! {__version__} --> {latest_remote_tag[1:]}" f"\n{latest_release_json['html_url']}" diff --git a/sherlock_project/sites.py b/sherlock_project/sites.py index b7aaf4c58b..5ac52de5bb 100644 --- a/sherlock_project/sites.py +++ b/sherlock_project/sites.py @@ -13,7 +13,7 @@ class SiteInformation: def __init__(self, name, url_home, url_username_format, username_claimed, - information, is_nsfw, username_unclaimed=secrets.token_urlsafe(10)): + information, is_nsfw, username_unclaimed=None): """Create Site Information Object. Contains information about a specific website. @@ -56,7 +56,7 @@ def __init__(self, name, url_home, url_username_format, username_claimed, self.url_username_format = url_username_format self.username_claimed = username_claimed - self.username_unclaimed = secrets.token_urlsafe(32) + self.username_unclaimed = username_unclaimed if username_unclaimed is not None else secrets.token_urlsafe(32) self.information = information self.is_nsfw = is_nsfw @@ -80,7 +80,7 @@ def __init__( self, data_file_path: str|None = None, honor_exclusions: bool = True, - do_not_exclude: list[str] = [], + do_not_exclude: list[str] | None = None, ): """Create Sites Information Object. @@ -115,10 +115,10 @@ def __init__( Nothing. """ + do_not_exclude = do_not_exclude if do_not_exclude is not None else [] + if not data_file_path: - # The default data file is the live data.json which is in the GitHub repo. The reason why we are using - # this instead of the local one is so that the user has the most up-to-date data. This prevents - # users from creating issue about false positives which has already been fixed or having outdated data + # The default data file is the live data.json from the GitHub repo. data_file_path = MANIFEST_URL # Ensure that specified data file has correct extension.