From 97d96613682e2bd9a853b53051bf3502c91b26ae Mon Sep 17 00:00:00 2001 From: beanh0le Date: Wed, 20 Apr 2022 21:06:52 -0400 Subject: [PATCH 1/5] sanitize input probably not completely right, need to check about sanitizing items passed though in update transaction and others --- App.py | 105 +++++++++++++++++++++++++++++++++++++++++++---- requirements.txt | 4 +- 2 files changed, 100 insertions(+), 9 deletions(-) diff --git a/App.py b/App.py index f84dcaf..9c74f7a 100644 --- a/App.py +++ b/App.py @@ -6,6 +6,7 @@ import datetime import os import traceback +import bleach from copy import deepcopy from functools import wraps from bson.objectid import ObjectId @@ -20,6 +21,7 @@ from flask_limiter.util import get_remote_address from Models import Person, Group, Item, TransactionItem, Transaction from mongoengine import * +from mongosanitizer.sanitizer import sanitize # setup the Flask server app = Flask(__name__) @@ -264,7 +266,11 @@ def user_profile(person): # if sub was given to us if 'sub' in request_data and request_data.get('sub') != person.sub: - person = Person.objects(sub=request_data.get('sub')) + sub_sanitized = request_data.get('sub') + sanitize(sub_sanitized) + if isinstance(sub_sanitized, str): + sub_sanitized = bleach.clean(sub_sanitized) + person = Person.objects(sub=sub_sanitized) if len(person) == 0: return jsonify({'msg': 'Token is unauthorized or user does not exist.'}), 404 person = person.first() @@ -299,6 +305,7 @@ def update_profile(person): # get fields request_data = request.get_json(force=True, silent=True) profile = request_data['data'] + sanitize(profile) # check for unallowed fields if set(profile.keys()).intersection({'email', 'sub', 'date_joined', 'picture', 'groups'}): @@ -312,9 +319,12 @@ def update_profile(person): # if key is pay_with must iterate through embedded dictionary elif k == 'pay_with': for k2, v2 in v.items(): + v2 = bleach.clean(v2) person[k][k2] = v2 # set the keyed value else: + if isinstance(v, str): + v = bleach.clean(v) person[k] = v # save the person @@ -377,6 +387,8 @@ def create_group(person): # get the request data request_data = request.get_json(force=True, silent=True) data = request_data.get('data') + sanitize(data) + if 'name' not in data: return jsonify({'msg': 'Missing Required Field(s) / Invalid Type(s).'}), 400 @@ -384,6 +396,11 @@ def create_group(person): group_desc = data.get('desc') invite = data.get('invites') + group_name = bleach.clean(group_name) + group_desc = bleach.clean(group_desc) + + + # create the group group = Group(name=group_name, desc=group_desc, admin=person.sub) @@ -410,6 +427,7 @@ def create_group(person): if not isinstance(invite, list): return jsonify({'msg': 'Missing Required Field(s) / Invalid Type(s).'}), 400 for email in invite: + email = bleach.clean(email) group.restricted.invite_list.append(email) # save the invite in the person @@ -442,6 +460,9 @@ def delete_group(person): request_data = request.get_json(force=True, silent=True) group_id = request_data['id'] + sanitize(group_id) + #group_id = bleach.clean(group_id) + # query the group group = Group.objects(id=group_id) if len(group) == 0 or person.sub != group.first().admin: @@ -491,6 +512,9 @@ def get_group(person): request_data = request.get_json(force=True, silent=True) group_id = request_data.get('id') + sanitize(group_id) + #group_id = bleach.clean(group_id) + if group_id is None: return jsonify({'msg': 'Missing Required Field(s) / Invalid Type(s).'}), 400 @@ -520,9 +544,14 @@ def update_group(person): """ # get the request data request_data = request.get_json(force=True, silent=True) + sanitize(request_data) group_id = request_data.get('id') data = request_data.get('data') + sanitize(group_id) + #group_id = bleach.clean(group_id) + sanitize(data) + # get the group group = Group.objects(id=group_id) if len(group) == 0: @@ -546,8 +575,12 @@ def update_group(person): if person.sub != group.admin: return jsonify({'msg': 'Token is unauthorized or group does not exist.'}), 404 for k3, v3 in v2.items(): - group[k][k3] = v3 + if isinstance(v3, str): + v3 = bleach.clean(v3) + group[k][k2][k3] = v3 else: + if isinstance(v, str): + v = bleach.clean(v) group[k] = v group.restricted.date.update = datetime.datetime.now(datetime.timezone.utc) @@ -578,6 +611,9 @@ def join_group(person): request_data = request.get_json(force=True, silent=True) group_id = request_data.get('id') + sanitize(group_id) + #bleach.clean(group_id) + # query the group group = Group.objects(id=group_id) @@ -634,6 +670,9 @@ def invite_group(person): request_data = request.get_json(force=True, silent=True) group_id = request_data.get('id') emails = request_data.get('emails') + + sanitize(group_id) + #group_id = bleach.clean(group_id) # query the group group = Group.objects(id=group_id) @@ -647,6 +686,8 @@ def invite_group(person): for email in emails: # check if already invited + sanitize(email) + email = bleach.clean(email) if email in group.restricted.invite_list: return jsonify({'msg': 'User is already a invited.'}), 409 @@ -689,7 +730,12 @@ def remove_member(person): request_data = request.get_json(force=True, silent=True) group_id = request_data.get('id') sub = request_data.get('userid') - + + sanitize(group_id) + sanitize(sub) + #group_id = bleach.clean(group_id) + if isinstance(sub, str): + sub = bleach.clean(sub) # query the group group = Group.objects(id=group_id) if len(group) == 0: @@ -742,6 +788,9 @@ def refresh_id(person): request_data = request.get_json(force=True, silent=True) group_id = request_data.get('id') + sanitize(group_id) + #group_id = bleach.clean(group_id) + # query the group group = Group.objects(id=group_id) if len(group) == 0: @@ -802,6 +851,27 @@ def create_transaction(person): who_paid = request_data.get('who_paid') date = request_data.get('date') items = request_data.get('items') + + sanitize(group_id) + sanitize(title) + sanitize(desc) + sanitize(vendor) + sanitize(who_paid) + sanitize(date) + sanitize(items) + + #if group_id not None: + #group_id = bleach.clean(group_id) + if title not None: + title = bleach.clean(title) + if desc not None: + desc = bleach.clean(desc) + if vendor not None: + vendor = bleach.clean(vendor) + + + + if group_id is None or title is None or items is None: return jsonify({'msg': 'Missing required field(s) or invalid type(s).'}), 400 @@ -856,7 +926,6 @@ def create_transaction(person): if items is not None: for item in items: person_id = item.get('owed_by') - # get the item data from the request name = item.get('name') desc = item.get('desc') @@ -865,6 +934,16 @@ def create_transaction(person): quantity = item.get('quantity') unit_price = item.get('unit_price') + + if person_id not None + person_id = bleach.clean(person_id) + if name not None + name = bleach.clean(name) + if desc not None + desc = bleach.clean(desc) + + + if total_price is None and (quantity is None or unit_price is None): transaction.delete() return jsonify({'msg': 'Missing required field(s) or invalid type(s).'}), 400 @@ -951,6 +1030,12 @@ def update_transaction(person): transaction_id = request_data.get('id') transaction_data = request_data.get('data') + sanitize(transaction_id) + sanitize(transaction_data) + + if transaction_id not None and isinstance(transaction_id, str): + bleach.clean(transaction_id) + if transaction_id is None or transaction_data is None: return jsonify({'msg': 'Missing required field(s) or invalid type(s).'}), 400 @@ -1021,15 +1106,17 @@ def update_transaction(person): # this assumes the user will pass the item information in the item field rather than the id _add_item_to_transaction(person, transaction_new, quantity=transaction_item['quantity'], - person_id=transaction_item['person'], - name=transaction_item['item']['name'], - desc=transaction_item['item']['desc'], + person_id=bleach.clean(transaction_item['person']), + name=bleach.clean(transaction_item['item']['name']), + desc=bleach.clean(transaction_item['item']['desc']), unit_price=transaction_item['item']['unit_price']) transaction_new.reload() # if normal string field else: # TODO - cross our fingers this will work + if isinstance(v, str): + v = bleach.clean(v) transaction_new[k] = v # update the last modified by @@ -1059,7 +1146,9 @@ def delete_transaction(person): if transaction_id is None: return jsonify({'msg': 'Missing required field(s) or invalid type(s).'}), 400 - + sanitize(transaction_id) + if isinstance(transaction_id, str): + bleach.clean(transaction_id) # query the transaction transaction = Transaction.objects.get(id=transaction_id) group_id = transaction.group diff --git a/requirements.txt b/requirements.txt index 0052aef..17ec66c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -25,4 +25,6 @@ google~=3.0.0 random-password-generator==2.2.0 flask_cors==3.0.10 Flask-Limiter==2.2.0 -flask2postman==1.4.4 \ No newline at end of file +flask2postman==1.4.4 +bleach==4.1.0 +MongoSanitizer==0.0.1 \ No newline at end of file From 177f3ad5a768fd4715348e725968f91a662436d6 Mon Sep 17 00:00:00 2001 From: beanh0le Date: Fri, 22 Apr 2022 09:14:38 -0400 Subject: [PATCH 2/5] most sanitation done i think sanitation is done, need to check types by try except when saving something in db (i think) --- App.py | 55 ++++++++++++++++++++++++++----------------------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/App.py b/App.py index 9c74f7a..be4b2ee 100644 --- a/App.py +++ b/App.py @@ -232,9 +232,9 @@ def register(): # if person not in DB create them if person is None: # create the person object - person = Person(first_name=token_info['given_name'], - last_name=token_info['family_name'], - email=token_info['email'], + person = Person(first_name=bleach.clean(token_info['given_name'], strip=True), + last_name=bleach.clean(token_info['family_name'], strip=True), + email=bleach.clean(token_info['email'], strip=True), sub=token_info['sub'], picture=token_info['picture']) @@ -268,8 +268,6 @@ def user_profile(person): if 'sub' in request_data and request_data.get('sub') != person.sub: sub_sanitized = request_data.get('sub') sanitize(sub_sanitized) - if isinstance(sub_sanitized, str): - sub_sanitized = bleach.clean(sub_sanitized) person = Person.objects(sub=sub_sanitized) if len(person) == 0: return jsonify({'msg': 'Token is unauthorized or user does not exist.'}), 404 @@ -319,8 +317,9 @@ def update_profile(person): # if key is pay_with must iterate through embedded dictionary elif k == 'pay_with': for k2, v2 in v.items(): - v2 = bleach.clean(v2) - person[k][k2] = v2 + if k2 in ['venmo', 'cashapp', 'paypal', 'preferred']: + v2 = bleach.clean(v2) + person[k][k2] = v2 # set the keyed value else: if isinstance(v, str): @@ -395,9 +394,10 @@ def create_group(person): group_name = data['name'] group_desc = data.get('desc') invite = data.get('invites') - + group_name = bleach.clean(group_name) - group_desc = bleach.clean(group_desc) + if isinstance(group_desc, str): + group_desc = bleach.clean(group_desc) @@ -461,7 +461,6 @@ def delete_group(person): group_id = request_data['id'] sanitize(group_id) - #group_id = bleach.clean(group_id) # query the group group = Group.objects(id=group_id) @@ -513,7 +512,6 @@ def get_group(person): group_id = request_data.get('id') sanitize(group_id) - #group_id = bleach.clean(group_id) if group_id is None: return jsonify({'msg': 'Missing Required Field(s) / Invalid Type(s).'}), 400 @@ -549,7 +547,6 @@ def update_group(person): data = request_data.get('data') sanitize(group_id) - #group_id = bleach.clean(group_id) sanitize(data) # get the group @@ -575,10 +572,12 @@ def update_group(person): if person.sub != group.admin: return jsonify({'msg': 'Token is unauthorized or group does not exist.'}), 404 for k3, v3 in v2.items(): - if isinstance(v3, str): - v3 = bleach.clean(v3) - group[k][k2][k3] = v3 + if k3 in ['only_admin_invite','only_admin_remove_user','only_owner_modify_transaction','admin_overrule_modify_transaction','user_delete_transaction','only_owner_delete_transaction','admin_overrule_delete_transaction',]: + if isinstance(v3, bool): + group[k][k2][k3] = v3 else: + #if k not in group: + # pass if isinstance(v, str): v = bleach.clean(v) group[k] = v @@ -612,7 +611,6 @@ def join_group(person): group_id = request_data.get('id') sanitize(group_id) - #bleach.clean(group_id) # query the group group = Group.objects(id=group_id) @@ -672,7 +670,6 @@ def invite_group(person): emails = request_data.get('emails') sanitize(group_id) - #group_id = bleach.clean(group_id) # query the group group = Group.objects(id=group_id) @@ -723,7 +720,7 @@ def remove_member(person): request must contain: - token - id: group id - - userid: [optional] user to remove from the grou + - userid: [optional] user to remove from the group :param person: the person making the request """ # get the request data @@ -733,7 +730,6 @@ def remove_member(person): sanitize(group_id) sanitize(sub) - #group_id = bleach.clean(group_id) if isinstance(sub, str): sub = bleach.clean(sub) # query the group @@ -789,7 +785,6 @@ def refresh_id(person): group_id = request_data.get('id') sanitize(group_id) - #group_id = bleach.clean(group_id) # query the group group = Group.objects(id=group_id) @@ -860,8 +855,7 @@ def create_transaction(person): sanitize(date) sanitize(items) - #if group_id not None: - #group_id = bleach.clean(group_id) + if title not None: title = bleach.clean(title) if desc not None: @@ -935,14 +929,17 @@ def create_transaction(person): unit_price = item.get('unit_price') - if person_id not None + if person_id not None and isinstance(person_id, str): person_id = bleach.clean(person_id) - if name not None + if name not None: name = bleach.clean(name) - if desc not None + if desc not None: desc = bleach.clean(desc) + if not isinstance(total_price, float) or not isinstance(quantity, int) or not isinstance(unit_price, float): + transaction.delete() + return jsonify({'msg': 'Missing required field(s) or invalid type(s).'}), 400 if total_price is None and (quantity is None or unit_price is None): transaction.delete() @@ -1033,8 +1030,7 @@ def update_transaction(person): sanitize(transaction_id) sanitize(transaction_data) - if transaction_id not None and isinstance(transaction_id, str): - bleach.clean(transaction_id) + if transaction_id is None or transaction_data is None: return jsonify({'msg': 'Missing required field(s) or invalid type(s).'}), 400 @@ -1069,6 +1065,8 @@ def update_transaction(person): # save the new transaction transaction_new.save() + #ASK_ + #unsure about this bc it is a dict of people if 'who_paid' in transaction_data: transaction_new.who_paid = transaction_data['who_paid'] @@ -1147,8 +1145,7 @@ def delete_transaction(person): if transaction_id is None: return jsonify({'msg': 'Missing required field(s) or invalid type(s).'}), 400 sanitize(transaction_id) - if isinstance(transaction_id, str): - bleach.clean(transaction_id) + # query the transaction transaction = Transaction.objects.get(id=transaction_id) group_id = transaction.group From 8c12b36d29cc4c65b890b1b35d19142785562fc1 Mon Sep 17 00:00:00 2001 From: beanh0le Date: Fri, 22 Apr 2022 11:51:18 -0400 Subject: [PATCH 3/5] sanitization --- App.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/App.py b/App.py index be4b2ee..26c1f2a 100644 --- a/App.py +++ b/App.py @@ -575,6 +575,8 @@ def update_group(person): if k3 in ['only_admin_invite','only_admin_remove_user','only_owner_modify_transaction','admin_overrule_modify_transaction','user_delete_transaction','only_owner_delete_transaction','admin_overrule_delete_transaction',]: if isinstance(v3, bool): group[k][k2][k3] = v3 + else + return jsonify({'msg': 'Missing Required Field(s) / Invalid Type(s).'}), 400 else: #if k not in group: # pass @@ -884,6 +886,11 @@ def create_transaction(person): if items is None and who_paid is None: return jsonify({'msg': 'Missing required field(s) or invalid type(s).'}), 400 + for v in who_paid.values(): + if v <= 0: + return jsonify({'msg': 'Missing required field(s) or invalid type(s).'}), 400 + + # create the transaction transaction = Transaction(title=title, group=group_id, @@ -937,10 +944,6 @@ def create_transaction(person): desc = bleach.clean(desc) - if not isinstance(total_price, float) or not isinstance(quantity, int) or not isinstance(unit_price, float): - transaction.delete() - return jsonify({'msg': 'Missing required field(s) or invalid type(s).'}), 400 - if total_price is None and (quantity is None or unit_price is None): transaction.delete() return jsonify({'msg': 'Missing required field(s) or invalid type(s).'}), 400 @@ -1254,6 +1257,10 @@ def _add_item_to_transaction(person, transaction, quantity, person_id, name, des if quantity < 1: raise Exception('Quantity cannot be less than 1.') + # check unit price for proper value + if unit_price < 0: + raise Exception('Unit price cannot be less than 0.') + # query the item to make sure it exists item = _create_item(name, desc, unit_price) item_cost = item.unit_price * quantity From 231f77ac5315a12e4dd503db84e313e0bb31ce5e Mon Sep 17 00:00:00 2001 From: beanh0le Date: Fri, 22 Apr 2022 12:29:00 -0400 Subject: [PATCH 4/5] syntax fix --- App.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/App.py b/App.py index 26c1f2a..2cb051d 100644 --- a/App.py +++ b/App.py @@ -22,6 +22,7 @@ from Models import Person, Group, Item, TransactionItem, Transaction from mongoengine import * from mongosanitizer.sanitizer import sanitize +from bleach import clean # setup the Flask server app = Flask(__name__) @@ -395,7 +396,7 @@ def create_group(person): group_desc = data.get('desc') invite = data.get('invites') - group_name = bleach.clean(group_name) + group_name = clean(group_name) if isinstance(group_desc, str): group_desc = bleach.clean(group_desc) @@ -575,7 +576,7 @@ def update_group(person): if k3 in ['only_admin_invite','only_admin_remove_user','only_owner_modify_transaction','admin_overrule_modify_transaction','user_delete_transaction','only_owner_delete_transaction','admin_overrule_delete_transaction',]: if isinstance(v3, bool): group[k][k2][k3] = v3 - else + else: return jsonify({'msg': 'Missing Required Field(s) / Invalid Type(s).'}), 400 else: #if k not in group: From 1c83593a529f03d3296419f3dea1937829a53f6d Mon Sep 17 00:00:00 2001 From: beanh0le Date: Fri, 22 Apr 2022 12:48:49 -0400 Subject: [PATCH 5/5] syntax changes --- App.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/App.py b/App.py index 951d4d7..9a71d0c 100644 --- a/App.py +++ b/App.py @@ -883,11 +883,11 @@ def create_transaction(person): sanitize(items) - if title not None: + if title is not None: title = bleach.clean(title) - if desc not None: + if desc is not None: desc = bleach.clean(desc) - if vendor not None: + if vendor is not None: vendor = bleach.clean(vendor) @@ -961,11 +961,11 @@ def create_transaction(person): unit_price = item.get('unit_price') - if person_id not None and isinstance(person_id, str): + if person_id is not None and isinstance(person_id, str): person_id = bleach.clean(person_id) - if name not None: + if name is not None: name = bleach.clean(name) - if desc not None: + if desc is not None: desc = bleach.clean(desc)