From 1b3fc55385f66c7d8f0466d10d7b1112f8d9a84f Mon Sep 17 00:00:00 2001 From: nick Date: Fri, 28 Feb 2020 08:03:01 -0400 Subject: [PATCH 01/13] Initial depth check setup, testing has also been completed --- api/app.py | 6 + api/backend/__init__.py | 1 + api/backend/depth_check.py | 45 ++++++++ api/resolvers/sectors.py | 53 +++++---- api/tests/test_depth_check.py | 207 ++++++++++++++++++++++++++++++++++ 5 files changed, 287 insertions(+), 25 deletions(-) create mode 100644 api/backend/__init__.py create mode 100644 api/backend/depth_check.py create mode 100644 api/tests/test_depth_check.py diff --git a/api/app.py b/api/app.py index 9ad9f6e533..fe712210cb 100644 --- a/api/app.py +++ b/api/app.py @@ -3,6 +3,9 @@ from flask import Flask from flask_graphql import GraphQLView from waitress import serve + +from backend import DepthAnalysisBackend + from db import ( db, DB_NAME, @@ -23,11 +26,14 @@ db.init_app(app) +backend = DepthAnalysisBackend() + app.add_url_rule( '/graphql', view_func=GraphQLView.as_view( 'graphql', schema=schema, + backend=DepthAnalysisBackend(), graphiql=True ) ) diff --git a/api/backend/__init__.py b/api/backend/__init__.py new file mode 100644 index 0000000000..45a5d3ea6a --- /dev/null +++ b/api/backend/__init__.py @@ -0,0 +1 @@ +from backend.depth_check import DepthAnalysisBackend diff --git a/api/backend/depth_check.py b/api/backend/depth_check.py new file mode 100644 index 0000000000..942bfc7e25 --- /dev/null +++ b/api/backend/depth_check.py @@ -0,0 +1,45 @@ +from graphql.backend.core import GraphQLCoreBackend +from graphql.language.ast import FragmentSpread, FragmentDefinition + + +def measure_depth(selection_set, level=1): + """ + This function calculates the depth of queries that have been requested from + the user. + :param selection_set: The query requested by a user + :param level: This is the max depth you want to check for + :return: Returns the depth of the current selection + """ + max_depth = level + for field in selection_set.selections: + if not isinstance(field, FragmentSpread): + if field.selection_set: + new_depth = measure_depth(field.selection_set, level=level + 1) + if new_depth > max_depth: + max_depth = new_depth + return max_depth + + +class DepthAnalysisBackend(GraphQLCoreBackend): + def document_from_string(self, schema, document_string): + """ + This function checks to see if the current query maxes the maximum depth + to prevent complexity attacks + :param schema: The schema of the application + :param document_string: The request from the user + :return: If the test passes it wil return the requested information, if + the test fails it will raise an exception and inform the user + """ + document = super().document_from_string(schema, document_string) + ast = document.document_ast + for definition in ast.definitions: + # We are only interested in queries + if not isinstance(definition, FragmentDefinition): + if definition.operation != 'query': + continue + if not isinstance(definition, FragmentSpread): + depth = measure_depth(definition.selection_set) + if depth > 10: # set your depth max here + raise Exception('Query is too complex') + + return document diff --git a/api/resolvers/sectors.py b/api/resolvers/sectors.py index 86c1fa70ab..621d743b3d 100644 --- a/api/resolvers/sectors.py +++ b/api/resolvers/sectors.py @@ -1,38 +1,41 @@ from graphql import GraphQLError from schemas.sectors import Sectors, SectorsModel from model_enums.sectors import SectorEnums +from manage import app # Resolvers def resolve_get_sector_by_id(self, info, **kwargs): - """Return a sector by its row ID""" - sector_id = kwargs.get('id', 1) - query = Sectors.get_query(info).filter( - SectorsModel.id == sector_id - ) - if not len(query.all()): - raise GraphQLError("Error, Invalid ID") - return query.all() + """Return a sector by its row ID""" + sector_id = kwargs.get('id', 1) + with app.app_context(): + query = Sectors.get_query(info).filter( + SectorsModel.id == sector_id + ) + if not len(query.all()): + raise GraphQLError("Error, Invalid ID") + return query.all() def resolve_get_sectors_by_sector(self, info, **kwargs): - """Return a list of sectors by its sector""" - sector = kwargs.get('sector', 'EMPTY') - query = Sectors.get_query(info).filter( - SectorsModel.sector == sector - ) - - if not len(query.all()): - raise GraphQLError("Error, Sector does not exist") - return query.all() + """Return a list of sectors by its sector""" + sector = kwargs.get('sector', 'EMPTY') + with app.app_context(): + query = Sectors.get_query(info).filter( + SectorsModel.sector == sector + ) + if not len(query.all()): + raise GraphQLError("Error, Sector does not exist") + return query.all() def resolve_get_sector_by_zone(self, info, **kwargs): - """Return a list of sectors by their zone""" - zone = kwargs.get('zone') - query = Sectors.get_query(info).filter( - SectorsModel.zone == zone - ) - if not len(query.all()): - raise GraphQLError("Error, Zone does not exist") - return query.all() + """Return a list of sectors by their zone""" + zone = kwargs.get('zone') + with app.app_context(): + query = Sectors.get_query(info).filter( + SectorsModel.zone == zone + ) + if not len(query.all()): + raise GraphQLError("Error, Zone does not exist") + return query.all() diff --git a/api/tests/test_depth_check.py b/api/tests/test_depth_check.py new file mode 100644 index 0000000000..4e5809a65c --- /dev/null +++ b/api/tests/test_depth_check.py @@ -0,0 +1,207 @@ +import sys +import os +from os.path import dirname, join, expanduser, normpath, realpath + +import pyotp +import pytest +from flask_bcrypt import Bcrypt +from graphene.test import Client + +from unittest import TestCase + +# This is the only way I could get imports to work for unit testing. +PACKAGE_PARENT = '..' +SCRIPT_DIR = dirname(realpath(join(os.getcwd(), expanduser(__file__)))) +sys.path.append(normpath(join(SCRIPT_DIR, PACKAGE_PARENT))) + +from manage import seed, remove_seed +seed() +from db import * +from app import app +from queries import schema +from models import Sectors, Groups +from functions.error_messages import * +from backend import DepthAnalysisBackend +remove_seed() + + +@pytest.fixture(scope='class') +def user_schema_test_db_init(): + db.init_app(app) + bcrypt = Bcrypt(app) + + with app.app_context(): + test_sector = Sectors( + id=1, + zone='ZO1', + sector='SO1' + ) + db.session.add(test_sector) + test_group = Groups( + id=1, + s_group='GO1', + sector_id=1 + ) + db.session.add(test_group) + db.session.commit() + + yield + + with app.app_context(): + Groups.query.delete() + Sectors.query.delete() + db.session.commit() + + +## +# This class of tests works within the 'createUser' api endpoint +@pytest.mark.usefixtures('user_schema_test_db_init') +class TestDepthCheck(TestCase): + def test_valid_depth_query(self): + backend = DepthAnalysisBackend() + client = Client(schema) + query = client.execute( + ''' + { + getSectorById(id: 1) { + groups { + edges { + node { + sectorId + } + } + } + } + } + ''', backend=backend) + result_refr = { + "data": { + "getSectorById": [ + { + "groups": { + "edges": [ + { + "node": { + "sectorId": 1 + } + } + ] + } + } + ] + } + } + self.assertDictEqual(result_refr, query) + + def test_invalid_depth_query(self): + backend = DepthAnalysisBackend() + client = Client(schema) + executed = client.execute( + ''' + { + getSectorById(id: 1) { + groups{ + edges{ + node{ + groupSector{ + groups{ + edges{ + node{ + groupSector{ + groups{ + edges{ + node{ + groupSector{ + groups{ + edges{ + node{ + groupSector{ + groups{ + edges{ + node{ + groupSector + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + ''', backend=backend) + result_refr = { + "data": { + "getSectorById": [ + { + "groups": { + "edges": [ + { + "node": { + "groupSector": { + "groups": { + "edges": [ + { + "node": { + "groupSector": { + "groups": { + "edges": [ + { + "node": { + "groupSector": { + "groups": { + "edges": [ + { + "node": { + "groupSector": { + "groups": { + "edges": [ + { + "node": { + "groupSector": { + "id": "U2VjdG9yczox" + } + } + } + ] + } + } + } + } + ] + } + } + } + } + ] + } + } + } + } + ] + } + } + } + } + ] + } + } + ] + } + } + # self.assertDictEqual(result_refr, executed) + assert executed['errors'] + assert executed['errors'][0] + assert executed['errors'][0]['message'] == 'Query is too complex' From 54c8b2d450bd735ca0f93912c675c4a5767ce3ba Mon Sep 17 00:00:00 2001 From: nick Date: Fri, 28 Feb 2020 12:54:59 -0400 Subject: [PATCH 02/13] Added set depth during creation of the depth check object --- api/app.py | 7 +++---- api/backend/depth_check.py | 6 +++++- api/tests/test_depth_check.py | 6 ++---- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/api/app.py b/api/app.py index fe712210cb..548cd6fd69 100644 --- a/api/app.py +++ b/api/app.py @@ -18,22 +18,21 @@ app = Flask(__name__) -app.config[ - 'SQLALCHEMY_DATABASE_URI'] = f'postgresql+psycopg2://{DB_USER}:{DB_PASS}@{DB_HOST}:{DB_PORT}/{DB_NAME}' +app.config['SQLALCHEMY_DATABASE_URI'] = f'postgresql+psycopg2://{DB_USER}:{DB_PASS}@{DB_HOST}:{DB_PORT}/{DB_NAME}' app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True app.debug = True db.init_app(app) -backend = DepthAnalysisBackend() +backend = DepthAnalysisBackend(10) app.add_url_rule( '/graphql', view_func=GraphQLView.as_view( 'graphql', schema=schema, - backend=DepthAnalysisBackend(), + backend=backend, graphiql=True ) ) diff --git a/api/backend/depth_check.py b/api/backend/depth_check.py index 942bfc7e25..2f053f7d60 100644 --- a/api/backend/depth_check.py +++ b/api/backend/depth_check.py @@ -21,6 +21,10 @@ def measure_depth(selection_set, level=1): class DepthAnalysisBackend(GraphQLCoreBackend): + def __init__(self, max_depth): + super().__init__() + self.max_depth = max_depth + def document_from_string(self, schema, document_string): """ This function checks to see if the current query maxes the maximum depth @@ -39,7 +43,7 @@ def document_from_string(self, schema, document_string): continue if not isinstance(definition, FragmentSpread): depth = measure_depth(definition.selection_set) - if depth > 10: # set your depth max here + if depth > self.max_depth: # set your depth max here raise Exception('Query is too complex') return document diff --git a/api/tests/test_depth_check.py b/api/tests/test_depth_check.py index 4e5809a65c..f65af8e7a1 100644 --- a/api/tests/test_depth_check.py +++ b/api/tests/test_depth_check.py @@ -20,7 +20,6 @@ from app import app from queries import schema from models import Sectors, Groups -from functions.error_messages import * from backend import DepthAnalysisBackend remove_seed() @@ -28,7 +27,6 @@ @pytest.fixture(scope='class') def user_schema_test_db_init(): db.init_app(app) - bcrypt = Bcrypt(app) with app.app_context(): test_sector = Sectors( @@ -58,7 +56,7 @@ def user_schema_test_db_init(): @pytest.mark.usefixtures('user_schema_test_db_init') class TestDepthCheck(TestCase): def test_valid_depth_query(self): - backend = DepthAnalysisBackend() + backend = DepthAnalysisBackend(10) client = Client(schema) query = client.execute( ''' @@ -94,7 +92,7 @@ def test_valid_depth_query(self): self.assertDictEqual(result_refr, query) def test_invalid_depth_query(self): - backend = DepthAnalysisBackend() + backend = DepthAnalysisBackend(10) client = Client(schema) executed = client.execute( ''' From c3b437997e6e99bf2c429bd079e3be993292d9ff Mon Sep 17 00:00:00 2001 From: nick Date: Mon, 2 Mar 2020 07:48:41 -0400 Subject: [PATCH 03/13] Add Check-Depth function to all tests that use the schema for testing --- api/backend/depth_check.py | 2 +- api/tests/test_auth_wrapper.py | 33 ++++++++++---------- api/tests/test_domains_resolver.py | 19 ++++++++---- api/tests/test_groups_resolver.py | 19 ++++++++---- api/tests/test_notification_emails.py | 14 ++++++--- api/tests/test_organizations_resolver.py | 19 ++++++++---- api/tests/test_scans_resolver.py | 37 ++++++++++++++-------- api/tests/test_sector_resolver.py | 19 ++++++++---- api/tests/test_user_roles.py | 39 ++++++++++++------------ api/tests/test_user_schema.py | 27 ++++++++++------ api/tests/test_user_sign_in.py | 7 +++-- 11 files changed, 145 insertions(+), 90 deletions(-) diff --git a/api/backend/depth_check.py b/api/backend/depth_check.py index 2f053f7d60..3a9489c96e 100644 --- a/api/backend/depth_check.py +++ b/api/backend/depth_check.py @@ -21,7 +21,7 @@ def measure_depth(selection_set, level=1): class DepthAnalysisBackend(GraphQLCoreBackend): - def __init__(self, max_depth): + def __init__(self, max_depth=10): super().__init__() self.max_depth = max_depth diff --git a/api/tests/test_auth_wrapper.py b/api/tests/test_auth_wrapper.py index 8f7df1653e..8087c79c58 100644 --- a/api/tests/test_auth_wrapper.py +++ b/api/tests/test_auth_wrapper.py @@ -20,13 +20,7 @@ from app import app from queries import schema from models import Users, User_affiliations, Organizations -from functions.error_messages import error_not_an_admin -from functions.auth_functions import ( - is_super_admin, - is_admin, - is_user_write, - is_user_read -) +from backend.depth_check import DepthAnalysisBackend remove_seed() @@ -113,6 +107,7 @@ def user_role_test_db_init(): class TestUserRole(TestCase): def test_user_read_claim(self): with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) get_token = client.execute( ''' @@ -121,7 +116,7 @@ def test_user_read_claim(self): authToken } } - ''') + ''', backend=backend) assert get_token['data']['signIn']['authToken'] is not None token = get_token['data']['signIn']['authToken'] assert token is not None @@ -137,13 +132,14 @@ def test_user_read_claim(self): { testUserClaims(org: ORG1, role: USER_READ) } - ''', context_value=request_headers) + ''', context_value=request_headers, backend=backend) assert executed['data'] assert executed['data']['testUserClaims'] assert executed['data']['testUserClaims'] == 'User Passed User Read Claim' def test_user_write_claim(self): with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) get_token = client.execute( ''' @@ -152,7 +148,7 @@ def test_user_write_claim(self): authToken } } - ''') + ''', backend=backend) assert get_token['data']['signIn']['authToken'] is not None token = get_token['data']['signIn']['authToken'] assert token is not None @@ -168,13 +164,14 @@ def test_user_write_claim(self): { testUserClaims(org: ORG1, role: USER_WRITE) } - ''', context_value=request_headers) + ''', context_value=request_headers, backend=backend) assert executed['data'] assert executed['data']['testUserClaims'] assert executed['data']['testUserClaims'] == 'User Passed User Write Claim' def test_admin_claim(self): with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) get_token = client.execute( ''' @@ -183,7 +180,7 @@ def test_admin_claim(self): authToken } } - ''') + ''', backend=backend) assert get_token['data']['signIn']['authToken'] is not None token = get_token['data']['signIn']['authToken'] assert token is not None @@ -199,13 +196,14 @@ def test_admin_claim(self): { testUserClaims(org: ORG1, role: ADMIN) } - ''', context_value=request_headers) + ''', context_value=request_headers, backend=backend) assert executed['data'] assert executed['data']['testUserClaims'] assert executed['data']['testUserClaims'] == 'User Passed Admin Claim' def test_super_admin_claim(self): with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) get_token = client.execute( ''' @@ -214,7 +212,7 @@ def test_super_admin_claim(self): authToken } } - ''') + ''', backend=backend) assert get_token['data']['signIn']['authToken'] is not None token = get_token['data']['signIn']['authToken'] assert token is not None @@ -230,13 +228,14 @@ def test_super_admin_claim(self): { testUserClaims(org: ORG1, role: SUPER_ADMIN) } - ''', context_value=request_headers) + ''', context_value=request_headers, backend=backend) assert executed['data'] assert executed['data']['testUserClaims'] assert executed['data']['testUserClaims'] == 'User Passed Super Admin Claim' def test_user_not_admin(self): with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) get_token = client.execute( ''' @@ -245,7 +244,7 @@ def test_user_not_admin(self): authToken } } - ''') + ''', backend=backend) assert get_token['data']['signIn']['authToken'] is not None token = get_token['data']['signIn']['authToken'] assert token is not None @@ -261,7 +260,7 @@ def test_user_not_admin(self): { testUserClaims(org: ORG1, role: ADMIN) } - ''', context_value=request_headers) + ''', context_value=request_headers, backend=backend) assert executed['errors'] assert executed['errors'][0] assert executed['errors'][0]['message'] == 'Error, user is not an admin for that org' diff --git a/api/tests/test_domains_resolver.py b/api/tests/test_domains_resolver.py index 289949a1e8..fe1486aec9 100644 --- a/api/tests/test_domains_resolver.py +++ b/api/tests/test_domains_resolver.py @@ -14,6 +14,7 @@ from db import db from models import Organizations, Domains from queries import schema +from backend.depth_check import DepthAnalysisBackend remove_seed() # This is the only way I could get imports to work for unit testing. @@ -55,6 +56,7 @@ class TestDomainsResolver(TestCase): def test_get_domain_resolvers_by_id(self): """Test get_domain_by_id resolver""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -72,12 +74,13 @@ def test_get_domain_resolvers_by_id(self): } } - result_eval = client.execute(query) + result_eval = client.execute(query, backend=backend) self.assertDictEqual(result_refr, result_eval) def test_get_domain_resolvers_by_domain(self): """"Test get_domain_by_domain resolver""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -95,12 +98,13 @@ def test_get_domain_resolvers_by_domain(self): } } - result_eval = client.execute(query) + result_eval = client.execute(query, backend=backend) self.assertDictEqual(result_refr, result_eval) def test_get_domain_resolvers_by_org(self): """Test get_domain_by_org_enum resolver""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -118,12 +122,13 @@ def test_get_domain_resolvers_by_org(self): } } - result_eval = client.execute(query) + result_eval = client.execute(query, backend=backend) self.assertDictEqual(result_refr, result_eval) def test_domain_resolver_by_id_invalid(self): """Test get_domain_by_id invalid ID error handling""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -131,7 +136,7 @@ def test_domain_resolver_by_id_invalid(self): domain } }""" - executed = client.execute(query) + executed = client.execute(query, backend=backend) assert executed['errors'] assert executed['errors'][0] @@ -140,6 +145,7 @@ def test_domain_resolver_by_id_invalid(self): def test_domain_resolver_by_url_invalid(self): """Test get_domain_by_domain invalid sector error handling""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -147,7 +153,7 @@ def test_domain_resolver_by_url_invalid(self): domain } }""" - executed = client.execute(query) + executed = client.execute(query, backend=backend) assert executed['errors'] assert executed['errors'][0] @@ -157,6 +163,7 @@ def test_domain_resolver_by_url_invalid(self): def test_domain_resolver_by_org_invalid(self): """Test get_domain_by_org invalid Zone error handling""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -164,7 +171,7 @@ def test_domain_resolver_by_org_invalid(self): domain } }""" - executed = client.execute(query) + executed = client.execute(query, backend=backend) assert executed['errors'] assert executed['errors'][0] diff --git a/api/tests/test_groups_resolver.py b/api/tests/test_groups_resolver.py index 48153e4bbf..8712bf78ff 100644 --- a/api/tests/test_groups_resolver.py +++ b/api/tests/test_groups_resolver.py @@ -14,6 +14,7 @@ from db import db from models import Sectors, Groups from queries import schema +from backend.depth_check import DepthAnalysisBackend remove_seed() # This is the only way I could get imports to work for unit testing. @@ -75,6 +76,7 @@ class TestGroupResolver(TestCase): def test_get_group_resolvers_by_id(self): """Test get_group_by_id resolver""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -94,12 +96,13 @@ def test_get_group_resolvers_by_id(self): } } - result_eval = client.execute(query) + result_eval = client.execute(query, backend=backend) self.assertDictEqual(result_refr, result_eval) def test_get_group_resolvers_by_group(self): """"Test get_group_by_group resolver""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -119,12 +122,13 @@ def test_get_group_resolvers_by_group(self): } } - result_eval = client.execute(query) + result_eval = client.execute(query, backend=backend) self.assertDictEqual(result_refr, result_eval) def test_get_group_resolvers_by_sector(self): """Test get_group_by_sector_id resolver""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -150,12 +154,13 @@ def test_get_group_resolvers_by_sector(self): } } - result_eval = client.execute(query) + result_eval = client.execute(query, backend=backend) self.assertDictEqual(result_refr, result_eval) def test_group_resolver_by_id_invalid(self): """Test get_group_by_id invalid ID error handling""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -165,7 +170,7 @@ def test_group_resolver_by_id_invalid(self): sectorId } }""" - executed = client.execute(query) + executed = client.execute(query, backend=backend) assert executed['errors'] assert executed['errors'][0] @@ -174,6 +179,7 @@ def test_group_resolver_by_id_invalid(self): def test_group_resolver_by_group_invalid(self): """Test get_group_by_group invalid sector error handling""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -183,7 +189,7 @@ def test_group_resolver_by_group_invalid(self): sectorId } }""" - executed = client.execute(query) + executed = client.execute(query, backend=backend) assert executed['errors'] assert executed['errors'][0] @@ -193,6 +199,7 @@ def test_group_resolver_by_group_invalid(self): def test_group_resolver_by_sector_invalid(self): """Test get_group_by_sector invalid Zone error handling""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -202,7 +209,7 @@ def test_group_resolver_by_sector_invalid(self): sectorId } }""" - executed = client.execute(query) + executed = client.execute(query, backend=backend) assert executed['errors'] assert executed['errors'][0] diff --git a/api/tests/test_notification_emails.py b/api/tests/test_notification_emails.py index dd037d5334..51022170ea 100644 --- a/api/tests/test_notification_emails.py +++ b/api/tests/test_notification_emails.py @@ -13,7 +13,7 @@ seed() from queries import schema - +from backend.depth_check import DepthAnalysisBackend remove_seed() # This is the only way I could get imports to work for unit testing. @@ -27,6 +27,7 @@ def test_email_sent_successfully(self): """Test for ensuring that an email is sent to a valid email address""" request_headers = {'Origin': "https://testserver.com"} with app.test_request_context(headers=request_headers): + backend = DepthAnalysisBackend() client = Client(schema) executed = client.execute( ''' @@ -40,7 +41,7 @@ def test_email_sent_successfully(self): } } } - ''') + ''', backend=backend) assert executed['data'] assert executed['data']['sendPasswordReset'] @@ -64,6 +65,7 @@ def test_email_sent_successfully(self): def test_invalid_email(self): """Tests to ensure that an invalid email address will raise an error""" + backend = DepthAnalysisBackend() client = Client(schema) executed = client.execute( ''' @@ -72,7 +74,7 @@ def test_invalid_email(self): id } } - ''') + ''', backend=backend) assert executed['errors'] assert executed['errors'][0] assert executed['errors'][0]['message'] == scalar_error_type( @@ -84,6 +86,7 @@ def test_email_sent_successfully(self): """Test for ensuring that an email is sent to a valid email address""" request_headers = {'Origin': "https://testserver.com"} with app.test_request_context(headers=request_headers): + backend = DepthAnalysisBackend() client = Client(schema) executed = client.execute( ''' @@ -97,7 +100,7 @@ def test_email_sent_successfully(self): } } } - ''') + ''', backend=backend) assert executed['data'] assert executed['data']['sendValidationEmail'] assert executed['data']['sendValidationEmail']['content'] @@ -121,6 +124,7 @@ def test_email_sent_successfully(self): def test_invalid_email(self): """Tests to ensure that an invalid email address will raise an error""" + backend = DepthAnalysisBackend() client = Client(schema) executed = client.execute( ''' @@ -129,7 +133,7 @@ def test_invalid_email(self): id } } - ''') + ''', backend=backend) assert executed['errors'] assert executed['errors'][0] assert executed['errors'][0]['message'] == scalar_error_type( diff --git a/api/tests/test_organizations_resolver.py b/api/tests/test_organizations_resolver.py index 5d72bb66dd..57d9e06c57 100644 --- a/api/tests/test_organizations_resolver.py +++ b/api/tests/test_organizations_resolver.py @@ -15,6 +15,7 @@ from db import db from models import Groups, Organizations from queries import schema +from backend.depth_check import DepthAnalysisBackend remove_seed() # This is the only way I could get imports to work for unit testing. @@ -57,6 +58,7 @@ class TestOrgResolver(TestCase): def test_get_org_resolvers_by_id(self): """Test get_organization_by_id resolver""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -76,12 +78,13 @@ def test_get_org_resolvers_by_id(self): } } - result_eval = client.execute(query) + result_eval = client.execute(query, backend=backend) self.assertDictEqual(result_refr, result_eval) def test_get_org_resolvers_by_org(self): """"Test get_org_by_org resolver""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -101,12 +104,13 @@ def test_get_org_resolvers_by_org(self): } } - result_eval = client.execute(query) + result_eval = client.execute(query, backend=backend) self.assertDictEqual(result_refr, result_eval) def test_get_org_resolvers_by_group(self): """Test get_org_by_group_id resolver""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -126,12 +130,13 @@ def test_get_org_resolvers_by_group(self): } } - result_eval = client.execute(query) + result_eval = client.execute(query, backend=backend) self.assertDictEqual(result_refr, result_eval) def test_org_resolver_by_id_invalid(self): """Test get_org_by_id invalid ID error handling""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -140,7 +145,7 @@ def test_org_resolver_by_id_invalid(self): groupId } }""" - executed = client.execute(query) + executed = client.execute(query, backend=backend) assert executed['errors'] assert executed['errors'][0] @@ -149,6 +154,7 @@ def test_org_resolver_by_id_invalid(self): def test_org_resolver_by_org_invalid(self): """Test get_org_by_org invalid sector error handling""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -157,7 +163,7 @@ def test_org_resolver_by_org_invalid(self): description } }""" - executed = client.execute(query) + executed = client.execute(query, backend=backend) assert executed['errors'] assert executed['errors'][0] @@ -166,6 +172,7 @@ def test_org_resolver_by_org_invalid(self): def test_org_resolver_by_group_invalid(self): """Test get_org_by_group invalid Zone error handling""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -174,7 +181,7 @@ def test_org_resolver_by_group_invalid(self): description } }""" - executed = client.execute(query) + executed = client.execute(query, backend=backend) assert executed['errors'] assert executed['errors'][0] diff --git a/api/tests/test_scans_resolver.py b/api/tests/test_scans_resolver.py index ab525b8d70..8f8eefaa12 100644 --- a/api/tests/test_scans_resolver.py +++ b/api/tests/test_scans_resolver.py @@ -16,6 +16,7 @@ from db import db from models import Scans, Domains, Users from queries import schema +from backend.depth_check import DepthAnalysisBackend remove_seed() # This is the only way I could get imports to work for unit testing. @@ -81,6 +82,7 @@ class TestScansResolver(TestCase): def test_get_scan_resolver_by_id(self): """Test get_sector_by_id resolver""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -106,13 +108,14 @@ def test_get_scan_resolver_by_id(self): } } - result_eval = client.execute(query) + result_eval = client.execute(query, backend=backend) self.assertDictEqual(result_refr, result_eval) def test_get_scans_by_date(self): """Test get_scans_by_date resolver""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -138,13 +141,14 @@ def test_get_scans_by_date(self): } } - result_eval = client.execute(query) + result_eval = client.execute(query, backend=backend) self.assertDictEqual(result_refr, result_eval) def test_scan_resolver_get_scans_by_date_range(self): """Test get_scans_by_date_range resolver""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -176,13 +180,14 @@ def test_scan_resolver_get_scans_by_date_range(self): } } - result_eval = client.execute(query) + result_eval = client.execute(query, backend=backend) self.assertDictEqual(result_refr, result_eval) def test_scan_resolver_get_scans_by_domain(self): """Test get_scans_by_domain resolver""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -214,13 +219,14 @@ def test_scan_resolver_get_scans_by_domain(self): } } - result_eval = client.execute(query) + result_eval = client.execute(query, backend=backend) self.assertDictEqual(result_refr, result_eval) def test_scan_resolver_get_scans_by_user_id(self): """Test get_scans_by_user_id resolver""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -252,13 +258,14 @@ def test_scan_resolver_get_scans_by_user_id(self): } } - result_eval = client.execute(query) + result_eval = client.execute(query, backend=backend) self.assertDictEqual(result_refr, result_eval) def test_scan_resolver_by_id_invalid(self): """Test get_scan_by_id invalid ID error handling""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -268,7 +275,7 @@ def test_scan_resolver_by_id_invalid(self): } } """ - executed = client.execute(query) + executed = client.execute(query, backend=backend) assert executed['errors'] assert executed['errors'][0] @@ -277,6 +284,7 @@ def test_scan_resolver_by_id_invalid(self): def test_scan_resolver_by_date_invalid(self): """Test get_scan_by_date invalid date error handling""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -285,7 +293,7 @@ def test_scan_resolver_by_date_invalid(self): } } """ - executed = client.execute(query) + executed = client.execute(query, backend=backend) assert executed['errors'] assert executed['errors'][0] @@ -294,6 +302,7 @@ def test_scan_resolver_by_date_invalid(self): def test_scan_resolver_by_date_range_invalid(self): """Test get_scan_by_date_range invalid date range error handling""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -302,7 +311,7 @@ def test_scan_resolver_by_date_range_invalid(self): } } """ - executed = client.execute(query) + executed = client.execute(query, backend=backend) assert executed['errors'] assert executed['errors'][0] @@ -311,6 +320,7 @@ def test_scan_resolver_by_date_range_invalid(self): def test_scan_resolver_by_nonexsiting_domain_invalid(self): """Test get_scan_by_domain invalid domain error handling""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -319,7 +329,7 @@ def test_scan_resolver_by_nonexsiting_domain_invalid(self): } } """ - executed = client.execute(query) + executed = client.execute(query, backend=backend) assert executed['errors'] assert executed['errors'][0] @@ -328,6 +338,7 @@ def test_scan_resolver_by_nonexsiting_domain_invalid(self): def test_scan_resolver_by_domain_invalid(self): """Test get_scan_by_domain no scan assocaited with that domain error handling""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -336,7 +347,7 @@ def test_scan_resolver_by_domain_invalid(self): } } """ - executed = client.execute(query) + executed = client.execute(query, backend=backend) assert executed['errors'] assert executed['errors'][0] @@ -345,6 +356,7 @@ def test_scan_resolver_by_domain_invalid(self): def test_scan_resolver_by_user_id_invalid_no_scans(self): """Test get_scan_by_user_id cannot find associated scans""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -353,7 +365,7 @@ def test_scan_resolver_by_user_id_invalid_no_scans(self): } } """ - executed = client.execute(query) + executed = client.execute(query, backend=backend) assert executed['errors'] assert executed['errors'][0] @@ -362,6 +374,7 @@ def test_scan_resolver_by_user_id_invalid_no_scans(self): def test_scan_resolver_by_user_invalid_id(self): """Test get_scan_by_user_id cannot find id""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -370,7 +383,7 @@ def test_scan_resolver_by_user_invalid_id(self): } } """ - executed = client.execute(query) + executed = client.execute(query, backend=backend) assert executed['errors'] assert executed['errors'][0] diff --git a/api/tests/test_sector_resolver.py b/api/tests/test_sector_resolver.py index c399b8412b..6108b034f4 100644 --- a/api/tests/test_sector_resolver.py +++ b/api/tests/test_sector_resolver.py @@ -14,6 +14,7 @@ from app import app from models import Sectors from queries import schema +from backend.depth_check import DepthAnalysisBackend remove_seed() # This is the only way I could get imports to work for unit testing. @@ -65,6 +66,7 @@ class TestSectorResolver(TestCase): def test_get_sector_resolver_by_id(self): """Test get_sector_by_id resolver""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -86,13 +88,14 @@ def test_get_sector_resolver_by_id(self): } } - result_eval = client.execute(query) + result_eval = client.execute(query, backend=backend) self.assertDictEqual(result_refr, result_eval) def test_get_sector_resolver_by_sector(self): """Test get_sector_by_sector resolver""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -112,13 +115,14 @@ def test_get_sector_resolver_by_sector(self): } } - result_eval = client.execute(query) + result_eval = client.execute(query, backend=backend) self.assertDictEqual(result_refr, result_eval) def test_get_sector_resolver_by_zone(self): """Test get_sector_by_zone resolver""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -138,13 +142,14 @@ def test_get_sector_resolver_by_zone(self): } } - result_eval = client.execute(query) + result_eval = client.execute(query, backend=backend) self.assertDictEqual(result_refr, result_eval) def test_sector_resolver_by_id_invalid(self): """Test get_sector_by_id invalid ID error handling""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -155,7 +160,7 @@ def test_sector_resolver_by_id_invalid(self): description } }""" - executed = client.execute(query) + executed = client.execute(query, backend=backend) assert executed['errors'] assert executed['errors'][0] @@ -164,6 +169,7 @@ def test_sector_resolver_by_id_invalid(self): def test_sector_resolver_by_sector_invalid(self): """Test get_sector_by_sector invalid sector error handling""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -173,7 +179,7 @@ def test_sector_resolver_by_sector_invalid(self): description } }""" - executed = client.execute(query) + executed = client.execute(query, backend=backend) assert executed['errors'] assert executed['errors'][0] @@ -182,6 +188,7 @@ def test_sector_resolver_by_sector_invalid(self): def test_sector_resolver_by_zone_invalid(self): """Test get_sector_by_zone invalid Zone error handling""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) query = """ { @@ -192,7 +199,7 @@ def test_sector_resolver_by_zone_invalid(self): description } }""" - executed = client.execute(query) + executed = client.execute(query, backend=backend) assert executed['errors'] assert executed['errors'][0] diff --git a/api/tests/test_user_roles.py b/api/tests/test_user_roles.py index 5c88a52414..c3eaaa3eb5 100644 --- a/api/tests/test_user_roles.py +++ b/api/tests/test_user_roles.py @@ -21,12 +21,7 @@ from queries import schema from models import Users, User_affiliations, Organizations from functions.error_messages import error_not_an_admin -from functions.auth_functions import ( - is_super_admin, - is_admin, - is_user_write, - is_user_read -) +from backend.depth_check import DepthAnalysisBackend remove_seed() @@ -85,6 +80,7 @@ def user_role_test_db_init(): class TestUserUpdateWriteRole(TestCase): def test_user_claim_update_to_user_write(self): with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) get_token = client.execute( ''' @@ -93,7 +89,7 @@ def test_user_claim_update_to_user_write(self): authToken } } - ''') + ''', backend=backend) assert get_token['data']['signIn']['authToken'] is not None token = get_token['data']['signIn']['authToken'] assert token is not None @@ -111,7 +107,7 @@ def test_user_claim_update_to_user_write(self): status } } - ''', context_value=request_headers) + ''', context_value=request_headers, backend=backend) assert executed['data'] assert executed['data']['updateUserRole'] assert executed['data']['updateUserRole']['status'] == 'Update Successful' @@ -124,7 +120,7 @@ def test_user_claim_update_to_user_write(self): authToken } } - ''') + ''', backend=backend) assert get_token['data']['signIn']['authToken'] is not None token = get_token['data']['signIn']['authToken'] assert token is not None @@ -140,7 +136,7 @@ def test_user_claim_update_to_user_write(self): { testUserClaims(org: ORG1, role: USER_WRITE) } - ''', context_value=request_headers) + ''', context_value=request_headers, backend=backend) assert executed['data'] assert executed['data']['testUserClaims'] assert executed['data']['testUserClaims'] == 'User Passed User Write Claim' @@ -150,6 +146,7 @@ def test_user_claim_update_to_user_write(self): class TestUserUpdateAdminRole(TestCase): def test_user_claim_update_to_admin(self): with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) get_token = client.execute( ''' @@ -158,7 +155,7 @@ def test_user_claim_update_to_admin(self): authToken } } - ''') + ''', backend=backend) assert get_token['data']['signIn']['authToken'] is not None token = get_token['data']['signIn']['authToken'] assert token is not None @@ -176,7 +173,7 @@ def test_user_claim_update_to_admin(self): status } } - ''', context_value=request_headers) + ''', context_value=request_headers, backend=backend) assert executed['data'] assert executed['data']['updateUserRole'] assert executed['data']['updateUserRole']['status'] == 'Update Successful' @@ -189,7 +186,7 @@ def test_user_claim_update_to_admin(self): authToken } } - ''') + ''', backend=backend) assert get_token['data']['signIn']['authToken'] is not None token = get_token['data']['signIn']['authToken'] assert token is not None @@ -205,7 +202,7 @@ def test_user_claim_update_to_admin(self): { testUserClaims(org: ORG1, role: ADMIN) } - ''', context_value=request_headers) + ''', context_value=request_headers, backend=backend) assert executed['data'] assert executed['data']['testUserClaims'] assert executed['data']['testUserClaims'] == 'User Passed Admin Claim' @@ -215,6 +212,7 @@ def test_user_claim_update_to_admin(self): class TestUserUpdateSuperAdminRole(TestCase): def test_user_claim_update_to_super_admin(self): with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) get_token = client.execute( ''' @@ -223,7 +221,7 @@ def test_user_claim_update_to_super_admin(self): authToken } } - ''') + ''', backend=backend) assert get_token['data']['signIn']['authToken'] is not None token = get_token['data']['signIn']['authToken'] assert token is not None @@ -241,7 +239,7 @@ def test_user_claim_update_to_super_admin(self): status } } - ''', context_value=request_headers) + ''', context_value=request_headers, backend=backend) assert executed['data'] assert executed['data']['updateUserRole'] assert executed['data']['updateUserRole']['status'] == 'Update Successful' @@ -254,7 +252,7 @@ def test_user_claim_update_to_super_admin(self): authToken } } - ''') + ''', backend=backend) assert get_token['data']['signIn']['authToken'] is not None token = get_token['data']['signIn']['authToken'] assert token is not None @@ -270,7 +268,7 @@ def test_user_claim_update_to_super_admin(self): { testUserClaims(org: ORG1, role: SUPER_ADMIN) } - ''', context_value=request_headers) + ''', context_value=request_headers, backend=backend) assert executed['data'] assert executed['data']['testUserClaims'] assert executed['data']['testUserClaims'] == 'User Passed Super Admin Claim' @@ -280,6 +278,7 @@ def test_user_claim_update_to_super_admin(self): class TestUserUpdateAdminRoleInvalid(TestCase): def test_user_claim_update_to_user_write(self): with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) get_token = client.execute( ''' @@ -288,7 +287,7 @@ def test_user_claim_update_to_user_write(self): authToken } } - ''') + ''', backend=backend) assert get_token['data']['signIn']['authToken'] is not None token = get_token['data']['signIn']['authToken'] assert token is not None @@ -306,7 +305,7 @@ def test_user_claim_update_to_user_write(self): status } } - ''', context_value=request_headers) + ''', context_value=request_headers, backend=backend) assert executed['errors'] assert executed['errors'][0] assert executed['errors'][0]['message'] == error_not_an_admin() diff --git a/api/tests/test_user_schema.py b/api/tests/test_user_schema.py index 1d923bc511..d0877429a1 100644 --- a/api/tests/test_user_schema.py +++ b/api/tests/test_user_schema.py @@ -19,6 +19,7 @@ from app import app from queries import schema from models import Users +from backend.depth_check import DepthAnalysisBackend from functions.error_messages import * remove_seed() @@ -59,6 +60,7 @@ class TestCreateUser: def test_successful_creation(self): """Test that ensures a user can be created successfully using the api endpoint""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) executed = client.execute( ''' @@ -71,7 +73,7 @@ def test_successful_creation(self): } } } - ''') + ''', backend=backend) assert executed['data'] assert executed['data']['createUser'] assert executed['data']['createUser']['user'] @@ -81,6 +83,7 @@ def test_successful_creation(self): def test_email_address_in_use(self): """Test that ensures each user has a unique email address""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) executed_first = client.execute( ''' @@ -92,7 +95,7 @@ def test_email_address_in_use(self): } } } - ''') + ''', backend=backend) executed = client.execute( ''' mutation{ @@ -103,7 +106,7 @@ def test_email_address_in_use(self): } } } - ''') + ''', backend=backend) assert executed['errors'] assert executed['errors'][0] @@ -111,6 +114,7 @@ def test_email_address_in_use(self): def test_password_too_short(self): """Test that ensure that a user's password meets the valid length requirements""" + backend = DepthAnalysisBackend() client = Client(schema) executed = client.execute( ''' @@ -121,7 +125,7 @@ def test_password_too_short(self): } } } - ''') + ''', backend=backend) assert executed['errors'] assert executed['errors'][0] @@ -129,6 +133,7 @@ def test_password_too_short(self): def test_passwords_do_not_match(self): """Test to ensure that user password matches their password confirmation""" + backend = DepthAnalysisBackend() client = Client(schema) executed = client.execute( ''' @@ -140,7 +145,7 @@ def test_passwords_do_not_match(self): } } } - ''') + ''', backend=backend) assert executed['errors'] assert executed['errors'][0] @@ -154,6 +159,7 @@ class TestUpdatePassword: def test_update_password_success(self): """Test to ensure that a user is returned when their password is updated successfully""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) executed = client.execute( ''' @@ -166,7 +172,7 @@ def test_update_password_success(self): } } } - ''') + ''', backend=backend) assert executed['data'] assert executed['data']['updatePassword'] @@ -176,6 +182,7 @@ def test_update_password_success(self): def test_updated_passwords_do_not_match(self): """Test to ensure that user's new password matches their password confirmation""" + backend = DepthAnalysisBackend() client = Client(schema) executed = client.execute( ''' @@ -187,7 +194,7 @@ def test_updated_passwords_do_not_match(self): } } } - ''') + ''', backend=backend) assert executed['errors'] assert executed['errors'][0] @@ -195,6 +202,7 @@ def test_updated_passwords_do_not_match(self): def test_updated_password_too_short(self): """Test that ensure that a user's password meets the valid length requirements""" + backend = DepthAnalysisBackend() client = Client(schema) executed = client.execute( ''' @@ -205,7 +213,7 @@ def test_updated_password_too_short(self): } } } - ''') + ''', backend=backend) assert executed['errors'] assert executed['errors'][0] @@ -213,6 +221,7 @@ def test_updated_password_too_short(self): def test_updated_password_no_user_email(self): """Test that ensures an empty string submitted as email will not be accepted""" + backend = DepthAnalysisBackend() client = Client(schema) executed = client.execute( ''' @@ -223,7 +232,7 @@ def test_updated_password_no_user_email(self): } } } - ''') + ''', backend=backend) assert executed['errors'] assert executed['errors'][0] diff --git a/api/tests/test_user_sign_in.py b/api/tests/test_user_sign_in.py index 80d1adb161..7a6d9f1c83 100644 --- a/api/tests/test_user_sign_in.py +++ b/api/tests/test_user_sign_in.py @@ -19,6 +19,7 @@ from app import app from queries import schema from models import Users +from backend.depth_check import DepthAnalysisBackend from functions.error_messages import( error_invalid_credentials ) @@ -54,6 +55,7 @@ class TestSignInUser: def test_successful_sign_in(self): """Test that ensures a user can be signed in""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) executed = client.execute( ''' @@ -64,7 +66,7 @@ def test_successful_sign_in(self): } } } - ''') + ''', backend=backend) assert executed['data'] assert executed['data']['signIn'] assert executed['data']['signIn']['user'] @@ -73,6 +75,7 @@ def test_successful_sign_in(self): def test_invalid_credentials(self): """Test that ensures a user can be signed in""" with app.app_context(): + backend = DepthAnalysisBackend() client = Client(schema) executed = client.execute( ''' @@ -83,7 +86,7 @@ def test_invalid_credentials(self): } } } - ''') + ''', backend=backend) assert executed['errors'] assert executed['errors'][0] assert executed['errors'][0]['message'] From 04da0aa1c16892103f9d9b09e4f1c82ebb648054 Mon Sep 17 00:00:00 2001 From: nick Date: Mon, 2 Mar 2020 08:52:30 -0400 Subject: [PATCH 04/13] Removed check for not query, as this will not check a mutation that contains a cycle --- api/backend/depth_check.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/api/backend/depth_check.py b/api/backend/depth_check.py index 3a9489c96e..74a1fcf9a7 100644 --- a/api/backend/depth_check.py +++ b/api/backend/depth_check.py @@ -39,8 +39,6 @@ def document_from_string(self, schema, document_string): for definition in ast.definitions: # We are only interested in queries if not isinstance(definition, FragmentDefinition): - if definition.operation != 'query': - continue if not isinstance(definition, FragmentSpread): depth = measure_depth(definition.selection_set) if depth > self.max_depth: # set your depth max here From fda995b574ea3c6d49648fd12d4c0d1af7cb9105 Mon Sep 17 00:00:00 2001 From: nick Date: Mon, 2 Mar 2020 09:31:51 -0400 Subject: [PATCH 05/13] Implemented better depth check analysis --- api/backend/depth_check.py | 123 ++++++++++++++++++++++++++----------- 1 file changed, 88 insertions(+), 35 deletions(-) diff --git a/api/backend/depth_check.py b/api/backend/depth_check.py index 74a1fcf9a7..48d194f319 100644 --- a/api/backend/depth_check.py +++ b/api/backend/depth_check.py @@ -1,47 +1,100 @@ +from typing import ( + Union, + Optional, + Any, + Dict, + List +) +from graphql import GraphQLDocument, GraphQLSchema from graphql.backend.core import GraphQLCoreBackend -from graphql.language.ast import FragmentSpread, FragmentDefinition +from graphql.language.ast import ( + Document, + FragmentSpread, + FragmentDefinition, + OperationDefinition, + Node, + FragmentSpread, + Field, + InlineFragment +) -def measure_depth(selection_set, level=1): +class DepthLimitReadched(Exception): + pass + + +def get_fragments(definitions) -> Dict[str, FragmentDefinition]: + return { + definition.name.value: definition + for definition in definitions + if isinstance(definition, FragmentDefinition) + } + + +def get_queries_and_mutations(definitions) -> List[OperationDefinition]: + return [ + definition + for definition in definitions + if isinstance(definition, OperationDefinition) + ] + + +def measure_depth(node: Node, fragments: Dict[str, FragmentDefinition]) -> int: """ - This function calculates the depth of queries that have been requested from - the user. - :param selection_set: The query requested by a user - :param level: This is the max depth you want to check for - :return: Returns the depth of the current selection + A function which recursively measures the depth of a Graphene Query + :type node: Node + :param node: Graphql-core object used for query traversal/indexing + :type fragments: dict + :param fragments: The fragments of the query + :rtype: int + :return: The max depth of the node """ - max_depth = level - for field in selection_set.selections: - if not isinstance(field, FragmentSpread): - if field.selection_set: - new_depth = measure_depth(field.selection_set, level=level + 1) - if new_depth > max_depth: - max_depth = new_depth - return max_depth + if isinstance(node, FragmentSpread): + fragment = fragments.get(node.name.value) + return measure_depth(node=fragment, fragments=fragments) + + elif isinstance(node, Field): + if node.name.value.lower() in ["__schema", "__introspection"]: + return 0 + if not node.selection_set: + return 1 + depths = [] + for selection in node.selection_set.selections: + depth = measure_depth(node=selection, fragments=fragments) + depths.append(depth) + return 1 + max(depths) + elif ( + isinstance(node, FragmentDefinition) + or isinstance(node, OperationDefinition) + or isinstance(node, InlineFragment) + ): + depths = [] + for selection in node.selection_set.selections: + depth = measure_depth(node=selection, fragments=fragments) + depths.append(depth) + return max(depths) + else: + raise Exception("Unknown node") + + +def check_max_depth(max_depth: int, document: Document): + fragments = get_fragments(document.definitions) + queries = get_queries_and_mutations(document.definitions) + + for query in queries: + depth = measure_depth(query, fragments) + if depth > max_depth: + raise DepthLimitReadched( + 'Query is too complex' + ) class DepthAnalysisBackend(GraphQLCoreBackend): - def __init__(self, max_depth=10): - super().__init__() + def __init__(self, max_depth=10, executor: Optional[Any] = None): + super().__init__(executor=executor) self.max_depth = max_depth - def document_from_string(self, schema, document_string): - """ - This function checks to see if the current query maxes the maximum depth - to prevent complexity attacks - :param schema: The schema of the application - :param document_string: The request from the user - :return: If the test passes it wil return the requested information, if - the test fails it will raise an exception and inform the user - """ + def document_from_string(self, schema: GraphQLSchema, document_string: Union[Document, str]) -> GraphQLDocument: document = super().document_from_string(schema, document_string) - ast = document.document_ast - for definition in ast.definitions: - # We are only interested in queries - if not isinstance(definition, FragmentDefinition): - if not isinstance(definition, FragmentSpread): - depth = measure_depth(definition.selection_set) - if depth > self.max_depth: # set your depth max here - raise Exception('Query is too complex') - + check_max_depth(max_depth=self.max_depth, document=document.document_ast) return document From 5f6eb4ff0d527fb36b407c6189fd1108f13572b1 Mon Sep 17 00:00:00 2001 From: nick Date: Mon, 2 Mar 2020 09:52:29 -0400 Subject: [PATCH 06/13] Typo in class definition --- api/backend/depth_check.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/backend/depth_check.py b/api/backend/depth_check.py index 48d194f319..f3170a0c8b 100644 --- a/api/backend/depth_check.py +++ b/api/backend/depth_check.py @@ -19,7 +19,7 @@ ) -class DepthLimitReadched(Exception): +class DepthLimitReached(Exception): pass From 0d5f7b664571391c5007c57569aeb8e5e954a027 Mon Sep 17 00:00:00 2001 From: nick Date: Mon, 2 Mar 2020 09:57:39 -0400 Subject: [PATCH 07/13] Fixed call to class --- api/backend/depth_check.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/backend/depth_check.py b/api/backend/depth_check.py index f3170a0c8b..d8a92bd063 100644 --- a/api/backend/depth_check.py +++ b/api/backend/depth_check.py @@ -84,7 +84,7 @@ def check_max_depth(max_depth: int, document: Document): for query in queries: depth = measure_depth(query, fragments) if depth > max_depth: - raise DepthLimitReadched( + raise DepthLimitReached( 'Query is too complex' ) From 93a8b5e426ea56c2654b502bd3d6d96181a102cc Mon Sep 17 00:00:00 2001 From: nick Date: Mon, 2 Mar 2020 11:59:13 -0400 Subject: [PATCH 08/13] Added in cost analysis check, with very basic cost map --- api/backend/cost_map.py | 5 +++++ api/backend/depth_check.py | 24 +++++++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 api/backend/cost_map.py diff --git a/api/backend/cost_map.py b/api/backend/cost_map.py new file mode 100644 index 0000000000..e42b7593b3 --- /dev/null +++ b/api/backend/cost_map.py @@ -0,0 +1,5 @@ +cost_map = { + 'getSectorById': 1, + 'getOrgById': 1, + 'getGroupById': 1 +} diff --git a/api/backend/depth_check.py b/api/backend/depth_check.py index d8a92bd063..123625850c 100644 --- a/api/backend/depth_check.py +++ b/api/backend/depth_check.py @@ -18,11 +18,17 @@ InlineFragment ) +from backend.cost_map import cost_map + class DepthLimitReached(Exception): pass +class CostLimitReached(Exception): + pass + + def get_fragments(definitions) -> Dict[str, FragmentDefinition]: return { definition.name.value: definition @@ -89,12 +95,28 @@ def check_max_depth(max_depth: int, document: Document): ) +def check_cost_analysis(max_cost: int, document: Document): + queries = get_queries_and_mutations(document.definitions) + total_cost = 0 + + for query in queries: + for selection in query.selection_set.selections: + current_select = selection.name.value + total_cost += cost_map[current_select] + if total_cost > max_cost: + raise CostLimitReached( + 'Query cost is too high' + ) + + class DepthAnalysisBackend(GraphQLCoreBackend): - def __init__(self, max_depth=10, executor: Optional[Any] = None): + def __init__(self, max_depth=10, max_cost=10, executor: Optional[Any] = None): super().__init__(executor=executor) self.max_depth = max_depth + self.max_cost = max_cost def document_from_string(self, schema: GraphQLSchema, document_string: Union[Document, str]) -> GraphQLDocument: document = super().document_from_string(schema, document_string) check_max_depth(max_depth=self.max_depth, document=document.document_ast) + check_cost_analysis(max_cost=self.max_cost, document=document.document_ast) return document From ad579406b336cacabc3f07fbece4b7c2b022a7f6 Mon Sep 17 00:00:00 2001 From: nick Date: Mon, 2 Mar 2020 14:41:55 -0400 Subject: [PATCH 09/13] Created new cost calculating method, updated name, and applied to all tests --- api/app.py | 4 +- api/backend/__init__.py | 2 +- api/backend/cost_check.py | 70 ++++++++++++++++++++++++ api/backend/depth_check.py | 39 ------------- api/backend/security_check.py | 24 ++++++++ api/tests/test_auth_wrapper.py | 12 ++-- api/tests/test_depth_check.py | 6 +- api/tests/test_domains_resolver.py | 14 ++--- api/tests/test_groups_resolver.py | 14 ++--- api/tests/test_notification_emails.py | 10 ++-- api/tests/test_organizations_resolver.py | 14 ++--- api/tests/test_scans_resolver.py | 26 ++++----- api/tests/test_sector_resolver.py | 14 ++--- api/tests/test_user_roles.py | 10 ++-- api/tests/test_user_schema.py | 27 +++++---- api/tests/test_user_sign_in.py | 6 +- 16 files changed, 175 insertions(+), 117 deletions(-) create mode 100644 api/backend/cost_check.py create mode 100644 api/backend/security_check.py diff --git a/api/app.py b/api/app.py index 548cd6fd69..90bc673760 100644 --- a/api/app.py +++ b/api/app.py @@ -4,7 +4,7 @@ from flask_graphql import GraphQLView from waitress import serve -from backend import DepthAnalysisBackend +from backend import SecurityAnalysisBackend from db import ( db, @@ -25,7 +25,7 @@ db.init_app(app) -backend = DepthAnalysisBackend(10) +backend = SecurityAnalysisBackend(max_depth=10, max_cost=1000) app.add_url_rule( '/graphql', diff --git a/api/backend/__init__.py b/api/backend/__init__.py index 45a5d3ea6a..cd521ba0ec 100644 --- a/api/backend/__init__.py +++ b/api/backend/__init__.py @@ -1 +1 @@ -from backend.depth_check import DepthAnalysisBackend +from backend.security_check import SecurityAnalysisBackend diff --git a/api/backend/cost_check.py b/api/backend/cost_check.py new file mode 100644 index 0000000000..6177225bdb --- /dev/null +++ b/api/backend/cost_check.py @@ -0,0 +1,70 @@ +from typing import ( + Dict +) +from graphql.language.ast import ( + Document, + FragmentDefinition, + OperationDefinition, + Node, + FragmentSpread, + Field, + InlineFragment +) + +from backend.depth_check import get_queries_and_mutations +from backend.depth_check import get_fragments +from backend.cost_map import cost_map + + +class CostLimitReached(Exception): + pass + + +def measure_cost(node: Node, fragments: Dict[str, FragmentDefinition]) -> int: + """ + A function which recursively measures the cost of a Graphene Query + :type node: Node + :param node: Graphql-core object used for query traversal/indexing + :type fragments: dict + :param fragments: The fragments of the query + :rtype: int + :return: The cost of the node + """ + if isinstance(node, FragmentSpread): + fragment = fragments.get(node.name.value) + return measure_cost(node=fragment, fragments=fragments) + + elif isinstance(node, Field): + if node.name.value.lower() in ["__schema", "__introspection"]: + return 0 + if not node.selection_set: + return cost_map.get(node.name.value, 1) + costs = [] + for selection in node.selection_set.selections: + cost = measure_cost(node=selection, fragments=fragments) + costs.append(cost) + return sum(costs) + cost_map.get(node.name.value, 1) + elif ( + isinstance(node, FragmentDefinition) + or isinstance(node, OperationDefinition) + or isinstance(node, InlineFragment) + ): + costs = [] + for selection in node.selection_set.selections: + cost = measure_cost(node=selection, fragments=fragments) + costs.append(cost) + return sum(costs) + else: + raise Exception("Unknown node") + + +def check_cost_analysis(max_cost: int, document: Document): + fragments = get_fragments(document.definitions) + queries = get_queries_and_mutations(document.definitions) + + for query in queries: + total_cost = measure_cost(query, fragments) + if total_cost > max_cost: + raise CostLimitReached( + 'Query cost is too high' + ) diff --git a/api/backend/depth_check.py b/api/backend/depth_check.py index 123625850c..21a70e4dd6 100644 --- a/api/backend/depth_check.py +++ b/api/backend/depth_check.py @@ -1,15 +1,9 @@ from typing import ( - Union, - Optional, - Any, Dict, List ) -from graphql import GraphQLDocument, GraphQLSchema -from graphql.backend.core import GraphQLCoreBackend from graphql.language.ast import ( Document, - FragmentSpread, FragmentDefinition, OperationDefinition, Node, @@ -18,17 +12,11 @@ InlineFragment ) -from backend.cost_map import cost_map - class DepthLimitReached(Exception): pass -class CostLimitReached(Exception): - pass - - def get_fragments(definitions) -> Dict[str, FragmentDefinition]: return { definition.name.value: definition @@ -93,30 +81,3 @@ def check_max_depth(max_depth: int, document: Document): raise DepthLimitReached( 'Query is too complex' ) - - -def check_cost_analysis(max_cost: int, document: Document): - queries = get_queries_and_mutations(document.definitions) - total_cost = 0 - - for query in queries: - for selection in query.selection_set.selections: - current_select = selection.name.value - total_cost += cost_map[current_select] - if total_cost > max_cost: - raise CostLimitReached( - 'Query cost is too high' - ) - - -class DepthAnalysisBackend(GraphQLCoreBackend): - def __init__(self, max_depth=10, max_cost=10, executor: Optional[Any] = None): - super().__init__(executor=executor) - self.max_depth = max_depth - self.max_cost = max_cost - - def document_from_string(self, schema: GraphQLSchema, document_string: Union[Document, str]) -> GraphQLDocument: - document = super().document_from_string(schema, document_string) - check_max_depth(max_depth=self.max_depth, document=document.document_ast) - check_cost_analysis(max_cost=self.max_cost, document=document.document_ast) - return document diff --git a/api/backend/security_check.py b/api/backend/security_check.py new file mode 100644 index 0000000000..c026605436 --- /dev/null +++ b/api/backend/security_check.py @@ -0,0 +1,24 @@ +from typing import ( + Union, + Optional, + Any +) +from graphql import GraphQLDocument, GraphQLSchema +from graphql.backend.core import GraphQLCoreBackend +from graphql.language.ast import Document + +from backend.depth_check import check_max_depth +from backend.cost_check import check_cost_analysis + + +class SecurityAnalysisBackend(GraphQLCoreBackend): + def __init__(self, max_depth=10, max_cost=1000, executor: Optional[Any] = None): + super().__init__(executor=executor) + self.max_depth = max_depth + self.max_cost = max_cost + + def document_from_string(self, schema: GraphQLSchema, document_string: Union[Document, str]) -> GraphQLDocument: + document = super().document_from_string(schema, document_string) + check_max_depth(max_depth=self.max_depth, document=document.document_ast) + check_cost_analysis(max_cost=self.max_cost, document=document.document_ast) + return document diff --git a/api/tests/test_auth_wrapper.py b/api/tests/test_auth_wrapper.py index 8087c79c58..f32d781aaf 100644 --- a/api/tests/test_auth_wrapper.py +++ b/api/tests/test_auth_wrapper.py @@ -20,7 +20,7 @@ from app import app from queries import schema from models import Users, User_affiliations, Organizations -from backend.depth_check import DepthAnalysisBackend +from backend.security_check import SecurityAnalysisBackend remove_seed() @@ -107,7 +107,7 @@ def user_role_test_db_init(): class TestUserRole(TestCase): def test_user_read_claim(self): with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) get_token = client.execute( ''' @@ -139,7 +139,7 @@ def test_user_read_claim(self): def test_user_write_claim(self): with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) get_token = client.execute( ''' @@ -171,7 +171,7 @@ def test_user_write_claim(self): def test_admin_claim(self): with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) get_token = client.execute( ''' @@ -203,7 +203,7 @@ def test_admin_claim(self): def test_super_admin_claim(self): with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) get_token = client.execute( ''' @@ -235,7 +235,7 @@ def test_super_admin_claim(self): def test_user_not_admin(self): with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) get_token = client.execute( ''' diff --git a/api/tests/test_depth_check.py b/api/tests/test_depth_check.py index f65af8e7a1..4d9b0238d1 100644 --- a/api/tests/test_depth_check.py +++ b/api/tests/test_depth_check.py @@ -20,7 +20,7 @@ from app import app from queries import schema from models import Sectors, Groups -from backend import DepthAnalysisBackend +from backend import SecurityAnalysisBackend remove_seed() @@ -56,7 +56,7 @@ def user_schema_test_db_init(): @pytest.mark.usefixtures('user_schema_test_db_init') class TestDepthCheck(TestCase): def test_valid_depth_query(self): - backend = DepthAnalysisBackend(10) + backend = SecurityAnalysisBackend(10) client = Client(schema) query = client.execute( ''' @@ -92,7 +92,7 @@ def test_valid_depth_query(self): self.assertDictEqual(result_refr, query) def test_invalid_depth_query(self): - backend = DepthAnalysisBackend(10) + backend = SecurityAnalysisBackend(10) client = Client(schema) executed = client.execute( ''' diff --git a/api/tests/test_domains_resolver.py b/api/tests/test_domains_resolver.py index fe1486aec9..82b683f49f 100644 --- a/api/tests/test_domains_resolver.py +++ b/api/tests/test_domains_resolver.py @@ -14,7 +14,7 @@ from db import db from models import Organizations, Domains from queries import schema -from backend.depth_check import DepthAnalysisBackend +from backend.security_check import SecurityAnalysisBackend remove_seed() # This is the only way I could get imports to work for unit testing. @@ -56,7 +56,7 @@ class TestDomainsResolver(TestCase): def test_get_domain_resolvers_by_id(self): """Test get_domain_by_id resolver""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -80,7 +80,7 @@ def test_get_domain_resolvers_by_id(self): def test_get_domain_resolvers_by_domain(self): """"Test get_domain_by_domain resolver""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -104,7 +104,7 @@ def test_get_domain_resolvers_by_domain(self): def test_get_domain_resolvers_by_org(self): """Test get_domain_by_org_enum resolver""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -128,7 +128,7 @@ def test_get_domain_resolvers_by_org(self): def test_domain_resolver_by_id_invalid(self): """Test get_domain_by_id invalid ID error handling""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -145,7 +145,7 @@ def test_domain_resolver_by_id_invalid(self): def test_domain_resolver_by_url_invalid(self): """Test get_domain_by_domain invalid sector error handling""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -163,7 +163,7 @@ def test_domain_resolver_by_url_invalid(self): def test_domain_resolver_by_org_invalid(self): """Test get_domain_by_org invalid Zone error handling""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { diff --git a/api/tests/test_groups_resolver.py b/api/tests/test_groups_resolver.py index 8712bf78ff..21bc8f1920 100644 --- a/api/tests/test_groups_resolver.py +++ b/api/tests/test_groups_resolver.py @@ -14,7 +14,7 @@ from db import db from models import Sectors, Groups from queries import schema -from backend.depth_check import DepthAnalysisBackend +from backend.security_check import SecurityAnalysisBackend remove_seed() # This is the only way I could get imports to work for unit testing. @@ -76,7 +76,7 @@ class TestGroupResolver(TestCase): def test_get_group_resolvers_by_id(self): """Test get_group_by_id resolver""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -102,7 +102,7 @@ def test_get_group_resolvers_by_id(self): def test_get_group_resolvers_by_group(self): """"Test get_group_by_group resolver""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -128,7 +128,7 @@ def test_get_group_resolvers_by_group(self): def test_get_group_resolvers_by_sector(self): """Test get_group_by_sector_id resolver""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -160,7 +160,7 @@ def test_get_group_resolvers_by_sector(self): def test_group_resolver_by_id_invalid(self): """Test get_group_by_id invalid ID error handling""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -179,7 +179,7 @@ def test_group_resolver_by_id_invalid(self): def test_group_resolver_by_group_invalid(self): """Test get_group_by_group invalid sector error handling""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -199,7 +199,7 @@ def test_group_resolver_by_group_invalid(self): def test_group_resolver_by_sector_invalid(self): """Test get_group_by_sector invalid Zone error handling""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { diff --git a/api/tests/test_notification_emails.py b/api/tests/test_notification_emails.py index 51022170ea..a0472f672c 100644 --- a/api/tests/test_notification_emails.py +++ b/api/tests/test_notification_emails.py @@ -13,7 +13,7 @@ seed() from queries import schema -from backend.depth_check import DepthAnalysisBackend +from backend.security_check import SecurityAnalysisBackend remove_seed() # This is the only way I could get imports to work for unit testing. @@ -27,7 +27,7 @@ def test_email_sent_successfully(self): """Test for ensuring that an email is sent to a valid email address""" request_headers = {'Origin': "https://testserver.com"} with app.test_request_context(headers=request_headers): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) executed = client.execute( ''' @@ -65,7 +65,7 @@ def test_email_sent_successfully(self): def test_invalid_email(self): """Tests to ensure that an invalid email address will raise an error""" - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) executed = client.execute( ''' @@ -86,7 +86,7 @@ def test_email_sent_successfully(self): """Test for ensuring that an email is sent to a valid email address""" request_headers = {'Origin': "https://testserver.com"} with app.test_request_context(headers=request_headers): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) executed = client.execute( ''' @@ -124,7 +124,7 @@ def test_email_sent_successfully(self): def test_invalid_email(self): """Tests to ensure that an invalid email address will raise an error""" - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) executed = client.execute( ''' diff --git a/api/tests/test_organizations_resolver.py b/api/tests/test_organizations_resolver.py index 57d9e06c57..ab2408b850 100644 --- a/api/tests/test_organizations_resolver.py +++ b/api/tests/test_organizations_resolver.py @@ -15,7 +15,7 @@ from db import db from models import Groups, Organizations from queries import schema -from backend.depth_check import DepthAnalysisBackend +from backend.security_check import SecurityAnalysisBackend remove_seed() # This is the only way I could get imports to work for unit testing. @@ -58,7 +58,7 @@ class TestOrgResolver(TestCase): def test_get_org_resolvers_by_id(self): """Test get_organization_by_id resolver""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -84,7 +84,7 @@ def test_get_org_resolvers_by_id(self): def test_get_org_resolvers_by_org(self): """"Test get_org_by_org resolver""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -110,7 +110,7 @@ def test_get_org_resolvers_by_org(self): def test_get_org_resolvers_by_group(self): """Test get_org_by_group_id resolver""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -136,7 +136,7 @@ def test_get_org_resolvers_by_group(self): def test_org_resolver_by_id_invalid(self): """Test get_org_by_id invalid ID error handling""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -154,7 +154,7 @@ def test_org_resolver_by_id_invalid(self): def test_org_resolver_by_org_invalid(self): """Test get_org_by_org invalid sector error handling""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -172,7 +172,7 @@ def test_org_resolver_by_org_invalid(self): def test_org_resolver_by_group_invalid(self): """Test get_org_by_group invalid Zone error handling""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { diff --git a/api/tests/test_scans_resolver.py b/api/tests/test_scans_resolver.py index 8f8eefaa12..1819ab2013 100644 --- a/api/tests/test_scans_resolver.py +++ b/api/tests/test_scans_resolver.py @@ -16,7 +16,7 @@ from db import db from models import Scans, Domains, Users from queries import schema -from backend.depth_check import DepthAnalysisBackend +from backend.security_check import SecurityAnalysisBackend remove_seed() # This is the only way I could get imports to work for unit testing. @@ -82,7 +82,7 @@ class TestScansResolver(TestCase): def test_get_scan_resolver_by_id(self): """Test get_sector_by_id resolver""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -115,7 +115,7 @@ def test_get_scan_resolver_by_id(self): def test_get_scans_by_date(self): """Test get_scans_by_date resolver""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -148,7 +148,7 @@ def test_get_scans_by_date(self): def test_scan_resolver_get_scans_by_date_range(self): """Test get_scans_by_date_range resolver""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -187,7 +187,7 @@ def test_scan_resolver_get_scans_by_date_range(self): def test_scan_resolver_get_scans_by_domain(self): """Test get_scans_by_domain resolver""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -226,7 +226,7 @@ def test_scan_resolver_get_scans_by_domain(self): def test_scan_resolver_get_scans_by_user_id(self): """Test get_scans_by_user_id resolver""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -265,7 +265,7 @@ def test_scan_resolver_get_scans_by_user_id(self): def test_scan_resolver_by_id_invalid(self): """Test get_scan_by_id invalid ID error handling""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -284,7 +284,7 @@ def test_scan_resolver_by_id_invalid(self): def test_scan_resolver_by_date_invalid(self): """Test get_scan_by_date invalid date error handling""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -302,7 +302,7 @@ def test_scan_resolver_by_date_invalid(self): def test_scan_resolver_by_date_range_invalid(self): """Test get_scan_by_date_range invalid date range error handling""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -320,7 +320,7 @@ def test_scan_resolver_by_date_range_invalid(self): def test_scan_resolver_by_nonexsiting_domain_invalid(self): """Test get_scan_by_domain invalid domain error handling""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -338,7 +338,7 @@ def test_scan_resolver_by_nonexsiting_domain_invalid(self): def test_scan_resolver_by_domain_invalid(self): """Test get_scan_by_domain no scan assocaited with that domain error handling""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -356,7 +356,7 @@ def test_scan_resolver_by_domain_invalid(self): def test_scan_resolver_by_user_id_invalid_no_scans(self): """Test get_scan_by_user_id cannot find associated scans""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -374,7 +374,7 @@ def test_scan_resolver_by_user_id_invalid_no_scans(self): def test_scan_resolver_by_user_invalid_id(self): """Test get_scan_by_user_id cannot find id""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { diff --git a/api/tests/test_sector_resolver.py b/api/tests/test_sector_resolver.py index 6108b034f4..8692f4d54f 100644 --- a/api/tests/test_sector_resolver.py +++ b/api/tests/test_sector_resolver.py @@ -14,7 +14,7 @@ from app import app from models import Sectors from queries import schema -from backend.depth_check import DepthAnalysisBackend +from backend.security_check import SecurityAnalysisBackend remove_seed() # This is the only way I could get imports to work for unit testing. @@ -66,7 +66,7 @@ class TestSectorResolver(TestCase): def test_get_sector_resolver_by_id(self): """Test get_sector_by_id resolver""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -95,7 +95,7 @@ def test_get_sector_resolver_by_id(self): def test_get_sector_resolver_by_sector(self): """Test get_sector_by_sector resolver""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -122,7 +122,7 @@ def test_get_sector_resolver_by_sector(self): def test_get_sector_resolver_by_zone(self): """Test get_sector_by_zone resolver""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -149,7 +149,7 @@ def test_get_sector_resolver_by_zone(self): def test_sector_resolver_by_id_invalid(self): """Test get_sector_by_id invalid ID error handling""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -169,7 +169,7 @@ def test_sector_resolver_by_id_invalid(self): def test_sector_resolver_by_sector_invalid(self): """Test get_sector_by_sector invalid sector error handling""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { @@ -188,7 +188,7 @@ def test_sector_resolver_by_sector_invalid(self): def test_sector_resolver_by_zone_invalid(self): """Test get_sector_by_zone invalid Zone error handling""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) query = """ { diff --git a/api/tests/test_user_roles.py b/api/tests/test_user_roles.py index c3eaaa3eb5..b434d72f32 100644 --- a/api/tests/test_user_roles.py +++ b/api/tests/test_user_roles.py @@ -21,7 +21,7 @@ from queries import schema from models import Users, User_affiliations, Organizations from functions.error_messages import error_not_an_admin -from backend.depth_check import DepthAnalysisBackend +from backend.security_check import SecurityAnalysisBackend remove_seed() @@ -80,7 +80,7 @@ def user_role_test_db_init(): class TestUserUpdateWriteRole(TestCase): def test_user_claim_update_to_user_write(self): with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) get_token = client.execute( ''' @@ -146,7 +146,7 @@ def test_user_claim_update_to_user_write(self): class TestUserUpdateAdminRole(TestCase): def test_user_claim_update_to_admin(self): with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) get_token = client.execute( ''' @@ -212,7 +212,7 @@ def test_user_claim_update_to_admin(self): class TestUserUpdateSuperAdminRole(TestCase): def test_user_claim_update_to_super_admin(self): with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) get_token = client.execute( ''' @@ -278,7 +278,7 @@ def test_user_claim_update_to_super_admin(self): class TestUserUpdateAdminRoleInvalid(TestCase): def test_user_claim_update_to_user_write(self): with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) get_token = client.execute( ''' diff --git a/api/tests/test_user_schema.py b/api/tests/test_user_schema.py index d0877429a1..cbe9508cfa 100644 --- a/api/tests/test_user_schema.py +++ b/api/tests/test_user_schema.py @@ -19,7 +19,7 @@ from app import app from queries import schema from models import Users -from backend.depth_check import DepthAnalysisBackend +from backend.security_check import SecurityAnalysisBackend from functions.error_messages import * remove_seed() @@ -60,7 +60,7 @@ class TestCreateUser: def test_successful_creation(self): """Test that ensures a user can be created successfully using the api endpoint""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) executed = client.execute( ''' @@ -83,7 +83,7 @@ def test_successful_creation(self): def test_email_address_in_use(self): """Test that ensures each user has a unique email address""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) executed_first = client.execute( ''' @@ -114,7 +114,7 @@ def test_email_address_in_use(self): def test_password_too_short(self): """Test that ensure that a user's password meets the valid length requirements""" - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) executed = client.execute( ''' @@ -133,7 +133,7 @@ def test_password_too_short(self): def test_passwords_do_not_match(self): """Test to ensure that user password matches their password confirmation""" - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) executed = client.execute( ''' @@ -159,7 +159,7 @@ class TestUpdatePassword: def test_update_password_success(self): """Test to ensure that a user is returned when their password is updated successfully""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) executed = client.execute( ''' @@ -182,7 +182,7 @@ def test_update_password_success(self): def test_updated_passwords_do_not_match(self): """Test to ensure that user's new password matches their password confirmation""" - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) executed = client.execute( ''' @@ -202,7 +202,7 @@ def test_updated_passwords_do_not_match(self): def test_updated_password_too_short(self): """Test that ensure that a user's password meets the valid length requirements""" - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) executed = client.execute( ''' @@ -221,7 +221,7 @@ def test_updated_password_too_short(self): def test_updated_password_no_user_email(self): """Test that ensures an empty string submitted as email will not be accepted""" - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) executed = client.execute( ''' @@ -246,6 +246,7 @@ class TestValidateTwoFactor: def test_successful_validation(self): """Test that ensures a validation is successful when all params are proper""" with app.app_context(): + backend = SecurityAnalysisBackend() totp = pyotp.TOTP('base32secret3232') otp_code = totp.now() # Generates a code that is valid for 30s. Plenty of time to execute the query @@ -259,7 +260,7 @@ def test_successful_validation(self): } } } - ''') + ''', backend=backend) assert executed['data'] assert executed['data']['authenticateTwoFactor'] assert executed['data']['authenticateTwoFactor']['user'] @@ -268,6 +269,7 @@ def test_successful_validation(self): def test_user_does_not_exist(self): """Test that an error is raised if the user specified does not exist""" with app.app_context(): + backend = SecurityAnalysisBackend() client = Client(schema) executed = client.execute( ''' @@ -278,7 +280,7 @@ def test_user_does_not_exist(self): } } } - ''') + ''', backend=backend) assert executed['errors'] assert executed['errors'][0] @@ -287,6 +289,7 @@ def test_user_does_not_exist(self): def test_invalid_otp_code(self): """Test that an error is raised if the user specified does not exist""" with app.app_context(): + backend=SecurityAnalysisBackend() client = Client(schema) executed = client.execute( ''' @@ -297,7 +300,7 @@ def test_invalid_otp_code(self): } } } - ''') + ''', backend=backend) assert executed['errors'] assert executed['errors'][0] diff --git a/api/tests/test_user_sign_in.py b/api/tests/test_user_sign_in.py index 7a6d9f1c83..717bb2c27f 100644 --- a/api/tests/test_user_sign_in.py +++ b/api/tests/test_user_sign_in.py @@ -19,7 +19,7 @@ from app import app from queries import schema from models import Users -from backend.depth_check import DepthAnalysisBackend +from backend.security_check import SecurityAnalysisBackend from functions.error_messages import( error_invalid_credentials ) @@ -55,7 +55,7 @@ class TestSignInUser: def test_successful_sign_in(self): """Test that ensures a user can be signed in""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) executed = client.execute( ''' @@ -75,7 +75,7 @@ def test_successful_sign_in(self): def test_invalid_credentials(self): """Test that ensures a user can be signed in""" with app.app_context(): - backend = DepthAnalysisBackend() + backend = SecurityAnalysisBackend() client = Client(schema) executed = client.execute( ''' From 38d2039be4e35f059871babdad321c092c21b2e2 Mon Sep 17 00:00:00 2001 From: nick Date: Tue, 3 Mar 2020 07:02:04 -0400 Subject: [PATCH 10/13] Added tests for costs, removed unused dicts for invalid tests --- api/tests/test_cost_check.py | 141 ++++++++++++++++++++++++++++++++++ api/tests/test_depth_check.py | 60 --------------- 2 files changed, 141 insertions(+), 60 deletions(-) create mode 100644 api/tests/test_cost_check.py diff --git a/api/tests/test_cost_check.py b/api/tests/test_cost_check.py new file mode 100644 index 0000000000..22f8ae7bf9 --- /dev/null +++ b/api/tests/test_cost_check.py @@ -0,0 +1,141 @@ +import sys +import os +from os.path import dirname, join, expanduser, normpath, realpath + +import pyotp +import pytest +from flask_bcrypt import Bcrypt +from graphene.test import Client + +from unittest import TestCase + +# This is the only way I could get imports to work for unit testing. +PACKAGE_PARENT = '..' +SCRIPT_DIR = dirname(realpath(join(os.getcwd(), expanduser(__file__)))) +sys.path.append(normpath(join(SCRIPT_DIR, PACKAGE_PARENT))) + +from manage import seed, remove_seed +seed() +from db import * +from app import app +from queries import schema +from models import Sectors, Groups +from backend import SecurityAnalysisBackend +remove_seed() + + +@pytest.fixture(scope='class') +def user_schema_test_db_init(): + db.init_app(app) + + with app.app_context(): + test_sector = Sectors( + id=1, + zone='ZO1', + sector='SO1' + ) + db.session.add(test_sector) + test_group = Groups( + id=1, + s_group='GO1', + sector_id=1 + ) + db.session.add(test_group) + db.session.commit() + + yield + + with app.app_context(): + Groups.query.delete() + Sectors.query.delete() + db.session.commit() + + +## +# This class of tests works within the 'createUser' api endpoint +@pytest.mark.usefixtures('user_schema_test_db_init') +class TestCostCheck(TestCase): + def test_valid_cost_query(self): + backend = SecurityAnalysisBackend() + client = Client(schema) + query = client.execute( + ''' + { + getSectorById(id: 1) { + groups { + edges { + node { + sectorId + } + } + } + } + } + ''', backend=backend) + result_refr = { + "data": { + "getSectorById": [ + { + "groups": { + "edges": [ + { + "node": { + "sectorId": 1 + } + } + ] + } + } + ] + } + } + self.assertDictEqual(result_refr, query) + + def test_invalid_cost_query(self): + backend = SecurityAnalysisBackend(10, 5) + client = Client(schema) + executed = client.execute( + ''' + { + getSectorById(id: 1) { + groups { + edges { + node { + sectorId + } + } + } + groups { + edges { + node { + sectorId + } + } + } + groups { + edges { + node { + sectorId + } + } + } + groups { + edges { + node { + sectorId + } + } + } + groups { + edges { + node { + sectorId + } + } + } + } + } + ''', backend=backend) + assert executed['errors'] + assert executed['errors'][0] + assert executed['errors'][0]['message'] == 'Query cost is too high' diff --git a/api/tests/test_depth_check.py b/api/tests/test_depth_check.py index 4d9b0238d1..d8868e8590 100644 --- a/api/tests/test_depth_check.py +++ b/api/tests/test_depth_check.py @@ -140,66 +140,6 @@ def test_invalid_depth_query(self): } } ''', backend=backend) - result_refr = { - "data": { - "getSectorById": [ - { - "groups": { - "edges": [ - { - "node": { - "groupSector": { - "groups": { - "edges": [ - { - "node": { - "groupSector": { - "groups": { - "edges": [ - { - "node": { - "groupSector": { - "groups": { - "edges": [ - { - "node": { - "groupSector": { - "groups": { - "edges": [ - { - "node": { - "groupSector": { - "id": "U2VjdG9yczox" - } - } - } - ] - } - } - } - } - ] - } - } - } - } - ] - } - } - } - } - ] - } - } - } - } - ] - } - } - ] - } - } - # self.assertDictEqual(result_refr, executed) assert executed['errors'] assert executed['errors'][0] assert executed['errors'][0]['message'] == 'Query is too complex' From f3761d3868c4c5e186b767348415eec379198fdf Mon Sep 17 00:00:00 2001 From: nick Date: Tue, 3 Mar 2020 07:04:13 -0400 Subject: [PATCH 11/13] Removed unused imports --- api/tests/test_cost_check.py | 4 +--- api/tests/test_depth_check.py | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/api/tests/test_cost_check.py b/api/tests/test_cost_check.py index 22f8ae7bf9..3e3f70c33f 100644 --- a/api/tests/test_cost_check.py +++ b/api/tests/test_cost_check.py @@ -2,9 +2,7 @@ import os from os.path import dirname, join, expanduser, normpath, realpath -import pyotp import pytest -from flask_bcrypt import Bcrypt from graphene.test import Client from unittest import TestCase @@ -16,7 +14,7 @@ from manage import seed, remove_seed seed() -from db import * +from db import db from app import app from queries import schema from models import Sectors, Groups diff --git a/api/tests/test_depth_check.py b/api/tests/test_depth_check.py index d8868e8590..c640de2f74 100644 --- a/api/tests/test_depth_check.py +++ b/api/tests/test_depth_check.py @@ -2,9 +2,7 @@ import os from os.path import dirname, join, expanduser, normpath, realpath -import pyotp import pytest -from flask_bcrypt import Bcrypt from graphene.test import Client from unittest import TestCase From b04e6d07d9851101a80589823e1f0ff542e942da Mon Sep 17 00:00:00 2001 From: nick Date: Tue, 3 Mar 2020 07:32:36 -0400 Subject: [PATCH 12/13] Cleaned up imports --- api/backend/__init__.py | 25 +++++++++++++++++++++++++ api/backend/cost_check.py | 6 ++++-- api/backend/depth_check.py | 26 ++++++-------------------- 3 files changed, 35 insertions(+), 22 deletions(-) diff --git a/api/backend/__init__.py b/api/backend/__init__.py index cd521ba0ec..cc01c30871 100644 --- a/api/backend/__init__.py +++ b/api/backend/__init__.py @@ -1 +1,26 @@ +from typing import ( + Dict, + List +) +from graphql.language.ast import ( + FragmentDefinition, + OperationDefinition +) + from backend.security_check import SecurityAnalysisBackend + + +def get_fragments(definitions) -> Dict[str, FragmentDefinition]: + return { + definition.name.value: definition + for definition in definitions + if isinstance(definition, FragmentDefinition) + } + + +def get_queries_and_mutations(definitions) -> List[OperationDefinition]: + return [ + definition + for definition in definitions + if isinstance(definition, OperationDefinition) + ] diff --git a/api/backend/cost_check.py b/api/backend/cost_check.py index 6177225bdb..8b4eddda57 100644 --- a/api/backend/cost_check.py +++ b/api/backend/cost_check.py @@ -10,9 +10,11 @@ Field, InlineFragment ) +from backend import ( + get_fragments, + get_queries_and_mutations +) -from backend.depth_check import get_queries_and_mutations -from backend.depth_check import get_fragments from backend.cost_map import cost_map diff --git a/api/backend/depth_check.py b/api/backend/depth_check.py index 21a70e4dd6..63ba6f566d 100644 --- a/api/backend/depth_check.py +++ b/api/backend/depth_check.py @@ -1,7 +1,4 @@ -from typing import ( - Dict, - List -) +from typing import Dict from graphql.language.ast import ( Document, FragmentDefinition, @@ -12,27 +9,16 @@ InlineFragment ) +from backend import ( + get_fragments, + get_queries_and_mutations +) + class DepthLimitReached(Exception): pass -def get_fragments(definitions) -> Dict[str, FragmentDefinition]: - return { - definition.name.value: definition - for definition in definitions - if isinstance(definition, FragmentDefinition) - } - - -def get_queries_and_mutations(definitions) -> List[OperationDefinition]: - return [ - definition - for definition in definitions - if isinstance(definition, OperationDefinition) - ] - - def measure_depth(node: Node, fragments: Dict[str, FragmentDefinition]) -> int: """ A function which recursively measures the depth of a Graphene Query From 967c6b85ea4ed2c4ec72ed50885a622dd0fb5336 Mon Sep 17 00:00:00 2001 From: nick Date: Tue, 3 Mar 2020 07:37:00 -0400 Subject: [PATCH 13/13] Fixed imports --- api/app.py | 2 +- api/backend/__init__.py | 2 -- api/tests/test_cost_check.py | 2 +- api/tests/test_depth_check.py | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/api/app.py b/api/app.py index 90bc673760..ef43fe1ebc 100644 --- a/api/app.py +++ b/api/app.py @@ -4,7 +4,7 @@ from flask_graphql import GraphQLView from waitress import serve -from backend import SecurityAnalysisBackend +from backend.security_check import SecurityAnalysisBackend from db import ( db, diff --git a/api/backend/__init__.py b/api/backend/__init__.py index cc01c30871..0524dc9484 100644 --- a/api/backend/__init__.py +++ b/api/backend/__init__.py @@ -7,8 +7,6 @@ OperationDefinition ) -from backend.security_check import SecurityAnalysisBackend - def get_fragments(definitions) -> Dict[str, FragmentDefinition]: return { diff --git a/api/tests/test_cost_check.py b/api/tests/test_cost_check.py index 3e3f70c33f..93ef8eb946 100644 --- a/api/tests/test_cost_check.py +++ b/api/tests/test_cost_check.py @@ -18,7 +18,7 @@ from app import app from queries import schema from models import Sectors, Groups -from backend import SecurityAnalysisBackend +from backend.security_check import SecurityAnalysisBackend remove_seed() diff --git a/api/tests/test_depth_check.py b/api/tests/test_depth_check.py index c640de2f74..6d148a01b1 100644 --- a/api/tests/test_depth_check.py +++ b/api/tests/test_depth_check.py @@ -18,7 +18,7 @@ from app import app from queries import schema from models import Sectors, Groups -from backend import SecurityAnalysisBackend +from backend.security_check import SecurityAnalysisBackend remove_seed()