Compare commits

..

No commits in common. "master" and "0.2.0" have entirely different histories.

2 changed files with 75 additions and 72 deletions

2
build.sh Executable file → Normal file
View File

@ -2,4 +2,4 @@
mkdir -p build
cd build
pyinstaller --distpath=../dist --workpath=../build -y ../src/have_I_b33n_pwned.py
pyinstaller --distpath=../dist --workpath=../build ../src/have_I_b33n_pwned.py

145
src/have_I_b33n_pwned.py Executable file → Normal file
View File

@ -21,83 +21,82 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# updater script for nextcloud
#
#TODO
# TODO
# - add feature: keepass integration? isnt there something like this already?
# - handle mail API exeption more precisely
from os import name
from sys import argv, stdout, exit
from re import match
from argparse import ArgumentParser
from hashlib import sha1
from getpass import getpass
import requests
from requests import get
if name == 'nt': # disable colors for windows environment
(RED, GREEN, RESET) = ("", "", "")
else:
(RED, GREEN, RESET) = ("\033[1;31m", "\033[0;32m", "\033[0;0m")
PASSWORD_API = 'https://api.pwnedpasswords.com/range/'
MAIL_API = 'https://haveibeenpwned.com/api/v2/breachedaccount/'
ROW = '{:<30}{:<10}{:<45}'
HIDDEN = True
INFINITE = True
RED = "\033[1;31m"
GREEN = "\033[0;32m"
RESET = "\033[0;0m"
API = 'https://api.pwnedpasswords.com/range/'
API_MAIL = 'https://haveibeenpwned.com/api/v2/breachedaccount/'
ROW = '{:<30}{:<10}{:<45}'
HIDDEN = False
parser = ArgumentParser(
description='Check if your email or password appears in a data leak.',
epilog='Moritz Münch, moritzmuench@mailbox.org, willipink.eu, GPL3+')
parser.add_argument('input',
nargs='*',
metavar='password or mail adress',
help='''You can specify passwords and mail adresses seperated by spaces.
If the password contains a space you have to enclose it in quotes like ' or
"''')
parser.add_argument('-s', '-show', '--show',
nargs='?',
help='Show entered in leak summary')
epilog='Moritz Münch, moritzmuench@mailbox.org, willipink.eu, GPL3+'
)
parser.add_argument('-m', '-mail', '--mail',
metavar='foo@adress.bar',
nargs='+',
help='Email address which should be checked against the leak database.'
)
parser.add_argument('-p', '-password', '--password',
metavar='password',
nargs='+',
help='Password which should be checked against the leak database.'
)
args = parser.parse_args()
if args.show:
HIDDEN = False
def input_prompt():
def header():
print()
print(ROW.format('password', 'leaked', 'sha1'))
print('-' * 80)
def prompt_password_or_mail():
print()
while True:
choice = input('Do you wannt to check a password [p] or an email adress [e]: ')
if choice == 'p':
prompt_password()
elif choice == 'e':
prompt_mail()
def prompt_password():
print()
password = getpass('Tell me your password: ')
global HIDDEN
if HIDDEN:
user_input = getpass('Tell me your password or mail adress: ')
else:
user_input = input('Tell me your password or mail adress: ')
check_mail_or_password(user_input)
HIDDEN = True
header()
query_password(password)
def check_mail_or_password(user_input):
valid_mail = match(r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$', user_input)
if valid_mail:
query_mail(valid_mail.group())
else:
query_password(user_input)
def no_connection_to(url):
''' Warn about no connection to *url* and exit '''
stdout.write(RED)
print('Could not connect to {}, please check your internet connection'.format(url))
stdout.write(RESET)
exit(1)
def prompt_mail():
print()
mail = input('Tell me your mail adress: ')
global HIDDEN
HIDDEN = True
header()
query_mail(mail)
def query_password(password):
''' Query the first 5 digits of the sha1 hash of the user input
ti the haveibeenpwned.com api. '''
password_hash = sha1(password.encode('UTF-8')).hexdigest().upper()
request = password_hash[:5]
try:
response = requests.get(PASSWORD_API + request).text
except requests.exceptions.ConnectionError:
no_connection_to(PASSWORD_API)
response = get(API + request).text
hash_searched = 'not yet'
for answer in response.splitlines():
data = answer.split(':')
@ -105,23 +104,26 @@ def query_password(password):
if password_hash == combined_hash:
hash_searched = int(data[1])
break
if hash_searched == 'not yet':
stdout.write(GREEN)
else:
stdout.write(RED)
if HIDDEN:
password = '*' * len(password)
print(ROW.format('password', 'leaked', 'sha1'))
print('-' * 80)
print(ROW.format(password, hash_searched, password_hash))
stdout.write(RESET)
if INFINITE:
input_prompt()
if HIDDEN:
prompt_password_or_mail()
def query_mail(mail):
print()
try:
response = requests.get(MAIL_API + mail).json()
response = get(API_MAIL + mail).json()
for breach in response:
print('Name: {}'.format(breach['Name']))
print('Title: {}'.format(breach['Title']))
@ -131,27 +133,28 @@ def query_mail(mail):
print('Modified date: {}'.format(breach['ModifiedDate']))
print('Pwn count: {}'.format(breach['PwnCount']))
print('Description: {}'.format(breach['Description']))
stdout.write(RED)
print('Breached data: {}'.format(', '.join([data for data in breach['DataClasses']])))
stdout.write(RESET)
print('Verified: {}'.format('yes' if breach['IsVerified'] == 1 else 'No'))
print('Fabricated: {}'.format('yes' if breach['IsFabricated'] == 1 else 'No'))
print('Sensitive: {}'.format('yes' if breach['IsSensitive'] == 1 else 'No'))
print('Retired: {}'.format('yes' if breach['IsRetired'] == 1 else 'No'))
print('Spam: {}'.format('yes' if breach['IsSpamList'] == 1 else 'No'))
except requests.exceptions.ConnectionError:
no_connection_to(MAIL_API)
except:
stdout.write(GREEN)
print('The mail adress {} is not in the leak database.'.format(mail))
stdout.write(RESET)
query_password(mail)
print('The mail adress {} was not found in any leak databases so far'.format(mail))
if HIDDEN:
prompt_password_or_mail()
if __name__ == '__main__':
if args.input:
for user_input in args.input:
check_mail_or_password(user_input)
if args.password:
header()
for password in args.password:
query_password(password)
elif args.mail:
for mail in args.mail:
query_mail(mail)
else:
INFINITE = True
input_prompt()
prompt_password_or_mail()
print()
exit(0)