Restructure all print output to use QueryNotifyPrint() object.

Added start and finish methods to base QueryNotify() object in order to get the same type of output.

The only thing not supported in this refactor is the exception text for an error status when we are in verbose mode.  This is an area of future work: I think exception information like this would more properly be handled by the logging module.
pull/590/head
Christopher K. Hoadley 5 years ago
parent ae2fd7a729
commit 6caa5a4e35

@ -4,6 +4,7 @@ This module defines the objects for notifying the caller about the
results of queries.
"""
from result import QueryStatus
from colorama import Fore, Style, init
class QueryNotify():
@ -33,6 +34,25 @@ class QueryNotify():
return
def start(self, message=None):
"""Notify Start.
Notify method for start of query. This method will be called before
any queries are performed. This method will typically be
overridden by higher level classes that will inherit from it.
Keyword Arguments:
self -- This object.
message -- Object that is used to give context to start
of query.
Default is None.
Return Value:
Nothing.
"""
return
def update(self, result):
"""Notify Update.
@ -52,6 +72,25 @@ class QueryNotify():
return
def finish(self, message=None):
"""Notify Finish.
Notify method for finish of query. This method will be called after
all queries have been performed. This method will typically be
overridden by higher level classes that will inherit from it.
Keyword Arguments:
self -- This object.
message -- Object that is used to give context to start
of query.
Default is None.
Return Value:
Nothing.
"""
return
def __str__(self):
"""Convert Object To String.
@ -65,3 +104,159 @@ class QueryNotify():
return result
def print_error(social_network, err, errstr, var, verbose=False, color=True):
if color:
print(Style.BRIGHT + Fore.WHITE + "[" +
Fore.RED + "-" +
Fore.WHITE + "]" +
Fore.GREEN + f" {social_network}:" +
Fore.RED + f" {errstr}" +
Fore.YELLOW + f" {err if verbose else var}")
else:
print(f"[-] {social_network}: {errstr} {err if verbose else var}")
def format_response_time(response_time, verbose):
return f" [{round(response_time * 1000)} ms]" if verbose else ""
def print_found(social_network, url, response_time, verbose=False, color=True):
if color:
print((Style.BRIGHT + Fore.WHITE + "[" +
Fore.GREEN + "+" +
Fore.WHITE + "]" +
format_response_time(response_time, verbose) +
Fore.GREEN + f" {social_network}:"), url)
else:
print(f"[+]{format_response_time(response_time, verbose)} {social_network}: {url}")
def print_not_found(social_network, response_time, verbose=False, color=True):
if color:
print((Style.BRIGHT + Fore.WHITE + "[" +
Fore.RED + "-" +
Fore.WHITE + "]" +
format_response_time(response_time, verbose) +
Fore.GREEN + f" {social_network}:" +
Fore.YELLOW + " Not Found!"))
else:
print(f"[-]{format_response_time(response_time, verbose)} {social_network}: Not Found!")
def print_invalid(social_network, msg, color=True):
"""Print invalid search result."""
if color:
print((Style.BRIGHT + Fore.WHITE + "[" +
Fore.RED + "-" +
Fore.WHITE + "]" +
Fore.GREEN + f" {social_network}:" +
Fore.YELLOW + f" {msg}"))
else:
print(f"[-] {social_network} {msg}")
class QueryNotifyPrint(QueryNotify):
"""Query Notify Print Object.
Query notify class that prints results.
"""
def __init__(self, result=None, verbose=False, print_found_only=False,
color=True):
"""Create Query Notify Print Object.
Contains information about a specific method of notifying the results
of a query.
Keyword Arguments:
self -- This object.
result -- Object of type QueryResult() containing
results for this query.
verbose -- Boolean indicating whether to give verbose output.
print_found_only -- Boolean indicating whether to only print found sites.
color -- Boolean indicating whether to color terminal output
Return Value:
Nothing.
"""
# Colorama module's initialization.
init(autoreset=True)
super().__init__(result)
self.verbose = verbose
self.print_found_only = print_found_only
self.color = color
return
def start(self, message):
"""Notify Start.
Will print the title to the standard output.
Keyword Arguments:
self -- This object.
message -- String containing username that the series
of queries are about.
Return Value:
Nothing.
"""
title = "Checking username"
if self.color:
print(Style.BRIGHT + Fore.GREEN + "[" +
Fore.YELLOW + "*" +
Fore.GREEN + f"] {title}" +
Fore.WHITE + f" {message}" +
Fore.GREEN + " on:")
else:
print(f"[*] {title} {message} on:")
return
def update(self, result):
"""Notify Update.
Will print the query result to the standard output.
Keyword Arguments:
self -- This object.
result -- Object of type QueryResult() containing
results for this query.
Return Value:
Nothing.
"""
self.result = result
#Output to the terminal is desired.
if result.status == QueryStatus.CLAIMED:
print_found(self.result.site_name, self.result.site_url_user, self.result.query_time, self.verbose, self.color)
elif result.status == QueryStatus.AVAILABLE:
if not self.print_found_only:
print_not_found(self.result.site_name, self.result.query_time, self.verbose, self.color)
elif result.status == QueryStatus.UNKNOWN:
print_error(self.result.site_name, "Exception Text", self.result.context, "", self.verbose, self.color)
elif result.status == QueryStatus.ILLEGAL:
if self.print_found_only == False:
print_invalid(self.result.site_name, "Illegal Username Format For This Site!", self.color)
else:
#It should be impossible to ever get here...
raise ValueError(f"Unknown Query Status '{str(result.status)}' for "
f"site '{self.result.site_name}'")
return
def __str__(self):
"""Convert Object To String.
Keyword Arguments:
self -- This object.
Return Value:
Nicely formatted string to get information about this object.
"""
result = str(self.result)
return result

@ -20,13 +20,13 @@ from time import time
import webbrowser
import requests
from colorama import Fore, Style, init
from requests_futures.sessions import FuturesSession
from torrequest import TorRequest
from result import QueryStatus
from result import QueryResult
from notify import QueryNotify
from notify import QueryNotifyPrint
from sites import SitesInformation
module_name = "Sherlock: Find Usernames Across Social Networks"
@ -98,66 +98,7 @@ class SherlockFuturesSession(FuturesSession):
*args, **kwargs)
def print_info(title, info, color=True):
if color:
print(Style.BRIGHT + Fore.GREEN + "[" +
Fore.YELLOW + "*" +
Fore.GREEN + f"] {title}" +
Fore.WHITE + f" {info}" +
Fore.GREEN + " on:")
else:
print(f"[*] {title} {info} on:")
def print_error(social_network, err, errstr, var, verbose=False, color=True):
if color:
print(Style.BRIGHT + Fore.WHITE + "[" +
Fore.RED + "-" +
Fore.WHITE + "]" +
Fore.GREEN + f" {social_network}:" +
Fore.RED + f" {errstr}" +
Fore.YELLOW + f" {err if verbose else var}")
else:
print(f"[-] {social_network}: {errstr} {err if verbose else var}")
def format_response_time(response_time, verbose):
return f" [{round(response_time * 1000)} ms]" if verbose else ""
def print_found(social_network, url, response_time, verbose=False, color=True):
if color:
print((Style.BRIGHT + Fore.WHITE + "[" +
Fore.GREEN + "+" +
Fore.WHITE + "]" +
format_response_time(response_time, verbose) +
Fore.GREEN + f" {social_network}:"), url)
else:
print(f"[+]{format_response_time(response_time, verbose)} {social_network}: {url}")
def print_not_found(social_network, response_time, verbose=False, color=True):
if color:
print((Style.BRIGHT + Fore.WHITE + "[" +
Fore.RED + "-" +
Fore.WHITE + "]" +
format_response_time(response_time, verbose) +
Fore.GREEN + f" {social_network}:" +
Fore.YELLOW + " Not Found!"))
else:
print(f"[-]{format_response_time(response_time, verbose)} {social_network}: Not Found!")
def print_invalid(social_network, msg, color=True):
"""Print invalid search result."""
if color:
print((Style.BRIGHT + Fore.WHITE + "[" +
Fore.RED + "-" +
Fore.WHITE + "]" +
Fore.GREEN + f" {social_network}:" +
Fore.YELLOW + f" {msg}"))
else:
print(f"[-] {social_network} {msg}")
def get_response(request_future, error_type, social_network, verbose=False, color=True):
def get_response(request_future, error_type, social_network):
#Default for Response object if some failure occurs.
response = None
@ -188,10 +129,9 @@ def get_response(request_future, error_type, social_network, verbose=False, colo
return response, error_context, expection_text
def sherlock(username, site_data, query_notify, verbose=False,
def sherlock(username, site_data, query_notify,
tor=False, unique_tor=False,
proxy=None, print_found_only=False, timeout=None, color=True,
print_output=True):
proxy=None, timeout=None):
"""Run Sherlock Analysis.
Checks for existence of username on various social media sites.
@ -203,16 +143,11 @@ def sherlock(username, site_data, query_notify, verbose=False,
query_notify -- Object with base type of QueryNotify().
This will be used to notify the caller about
query results.
verbose -- Boolean indicating whether to give verbose output.
tor -- Boolean indicating whether to use a tor circuit for the requests.
unique_tor -- Boolean indicating whether to use a new tor circuit for each request.
proxy -- String indicating the proxy URL
print_found_only -- Boolean indicating whether to only print found sites.
timeout -- Time in seconds to wait before timing out request.
Default is no timeout.
color -- Boolean indicating whether to color terminal output
print_output -- Boolean indicating whether the output should be
printed. Default is True.
Return Value:
Dictionary containing results from report. Key of dictionary is the name
@ -227,8 +162,9 @@ def sherlock(username, site_data, query_notify, verbose=False,
response_text: Text that came back from request. May be None if
there was an HTTP error when checking for existence.
"""
if print_output == True:
print_info("Checking username", username, color)
#Notify caller that we are starting the query.
query_notify.start(username)
# Create session based on request methodology
if tor or unique_tor:
@ -281,9 +217,6 @@ def sherlock(username, site_data, query_notify, verbose=False,
regex_check = net_info.get("regexCheck")
if regex_check and re.search(regex_check, username) is None:
# No need to do the check at the site: this user name is not allowed.
if (print_output == True) and not print_found_only:
print_invalid(social_network, "Illegal Username Format For This Site!", color)
results_site['status'] = QueryResult(username,
social_network,
url,
@ -365,9 +298,7 @@ def sherlock(username, site_data, query_notify, verbose=False,
future = net_info["request_future"]
r, error_text, expection_text = get_response(request_future=future,
error_type=error_type,
social_network=social_network,
verbose=verbose,
color=color)
social_network=social_network)
#Get response time for response of our request.
try:
@ -448,24 +379,6 @@ def sherlock(username, site_data, query_notify, verbose=False,
#Notify caller about results of query.
query_notify.update(result)
if print_output == True:
#Output to the terminal is desired.
if result.status == QueryStatus.CLAIMED:
print_found(social_network, url, response_time, verbose, color)
elif result.status == QueryStatus.AVAILABLE:
if not print_found_only:
print_not_found(social_network, response_time, verbose, color)
elif result.status == QueryStatus.UNKNOWN:
print_error(social_network, expection_text, error_text, "", verbose, color)
elif result.status == QueryStatus.ILLEGAL:
if not print_found_only:
print_invalid(social_network, "Illegal Username Format For This Site!", color)
else:
#It should be impossible to ever get here...
raise ValueError(f"Unknown Query Status '{str(result.status)}' for "
f"site '{social_network}'")
# Save status of request
results_site['status'] = result
@ -475,6 +388,10 @@ def sherlock(username, site_data, query_notify, verbose=False,
# Add this site's results into final dictionary with all of the other results.
results_total[social_network] = results_site
#Notify caller that all queries are finished.
query_notify.finish()
return results_total
@ -504,8 +421,6 @@ def timeout_check(value):
def main():
# Colorama module's initialization.
init(autoreset=True)
version_string = f"%(prog)s {__version__}\n" + \
f"{requests.__description__}: {requests.__version__}\n" + \
@ -651,7 +566,10 @@ def main():
#Create notify object for query results.
query_notify = QueryNotify()
query_notify = QueryNotifyPrint(result=None,
verbose=args.verbose,
print_found_only=args.print_found_only,
color=not args.no_color)
# Run report on all specified users.
for username in args.username:
@ -660,13 +578,10 @@ def main():
results = sherlock(username,
site_data,
query_notify,
verbose=args.verbose,
tor=args.tor,
unique_tor=args.unique_tor,
proxy=args.proxy,
print_found_only=args.print_found_only,
timeout=args.timeout,
color=not args.no_color)
timeout=args.timeout)
if args.output:
result_file = args.output

@ -118,12 +118,9 @@ class SherlockBaseTest(unittest.TestCase):
results = sherlock.sherlock(username,
site_data,
self.query_notify,
verbose=self.verbose,
tor=self.tor,
unique_tor=self.unique_tor,
timeout=self.timeout,
color=False,
print_output=False
timeout=self.timeout
)
for site, result in results.items():
with self.subTest(f"Checking Username '{username}' "

Loading…
Cancel
Save