def test_publish_requires_version(): vuln = Vulnerability(state=VulnerabilityState.REVIEWED) with pytest.raises(TransitionDenied): vuln.update_state(VulnerabilityState.PUBLISHED) assert vuln.state == VulnerabilityState.REVIEWED
def test_mark_needs_improvement_as_reviewable(): cr1 = creator1() vuln = Vulnerability(state=VulnerabilityState.NEEDS_IMPROVEMENT, creator=cr1) vuln.make_reviewable() assert vuln.state == VulnerabilityState.READY assert vuln.is_reviewable
def test_take_review_requires_reviewer(): cr1 = creator1() vuln = Vulnerability(state=VulnerabilityState.READY, creator=cr1) with pytest.raises(TransitionDenied): vuln.update_state(VulnerabilityState.IN_REVIEW) assert vuln.state == VulnerabilityState.READY
def test_mark_new_as_reviewable(): cr1 = creator1() vuln = Vulnerability(state=VulnerabilityState.NEW, creator=cr1) vuln.make_reviewable() assert vuln.state == VulnerabilityState.READY assert vuln.is_reviewable()
def test_deny_change_requires_feedback(): vuln = Vulnerability(state=VulnerabilityState.IN_REVIEW) with pytest.raises(TransitionDenied): vuln.update_state(VulnerabilityState.NEEDS_IMPROVEMENT) assert vuln.state == VulnerabilityState.IN_REVIEW
def test_return_review_requires_reviewer_reset(): rev = reviewer() vuln = Vulnerability(state=VulnerabilityState.IN_REVIEW, reviewer=rev) with pytest.raises(TransitionDenied): vuln.update_state(VulnerabilityState.READY) assert vuln.state == VulnerabilityState.IN_REVIEW
def test_deny_reviewed(): vuln = Vulnerability(state=VulnerabilityState.REVIEWED) rev = reviewer() vuln.deny_change(rev, "merge conflict") assert vuln.state == VulnerabilityState.NEEDS_IMPROVEMENT assert vuln.review_feedback == "merge conflict" assert not vuln.is_publishable assert not vuln.is_reviewable
def test_take_review(): cr1 = creator1() rev = reviewer() vuln = Vulnerability(state=VulnerabilityState.READY, creator=cr1) vuln.accept_review(rev) assert vuln.reviewer == rev assert vuln.state == VulnerabilityState.IN_REVIEW assert vuln.is_reviewer(rev)
def test_return_review(): rev = reviewer() vuln = Vulnerability(state=VulnerabilityState.IN_REVIEW, reviewer=rev) vuln.return_to_review_pool() assert vuln.state == VulnerabilityState.READY assert vuln.reviewer is None assert not vuln.is_publishable assert vuln.is_reviewable
def test_publish_reviewed(mocker): mocker.patch.object(Vulnerability, "next_version_number").return_value = 123 vuln = Vulnerability(state=VulnerabilityState.REVIEWED, version=1) vuln.publish_change() assert vuln.state == VulnerabilityState.PUBLISHED assert vuln.version == 123 assert not vuln.is_publishable assert not vuln.is_in_review
def test_archive_published(mocker): # !! mocks all .query getters on all models. Save here as we only use one mock_query = mocker.patch("flask_sqlalchemy._QueryProperty.__get__").return_value mock_query.filter.return_value.exists.return_value = True vuln = Vulnerability(state=VulnerabilityState.PUBLISHED) vuln.archive_entry() assert vuln.state == VulnerabilityState.ARCHIVED assert not vuln.is_publishable assert not vuln.is_in_review
def test_archive_requires_published(mocker): # !! mocks all .query getters on all models. Save here as we only use one mock_query = mocker.patch("flask_sqlalchemy._QueryProperty.__get__").return_value mock_query.filter.return_value.first.return_value = False vuln = Vulnerability(state=VulnerabilityState.PUBLISHED) with pytest.raises(TransitionDenied): vuln.archive_entry() assert vuln.state == VulnerabilityState.PUBLISHED assert not vuln.is_publishable assert not vuln.is_in_review
def test_delete_vulnerability_entry(client): vuln = Vulnerability.get_by_cve_id('CVE-1970-1000') assert vuln is not None resp = client.post('/CVE-1970-1000/create', data={ 'delete_entry': vuln.id, }) assert resp.status_code == 401 vuln = Vulnerability.get_by_cve_id('CVE-1970-1000') assert vuln is not None
def test_update_vulnerability(client, db_session, use_group, expected_status): if use_group: use_group(client) data = { 'cve_id': 'CVE-1970-1000', 'comment': 'This is the new comment', 'commits-0-commit_link': 'https://github.com/OWNER/REPO/commit/12345678', 'commits-0-repo_name': 'REPO', 'commits-0-repo_url': 'https://github.com/OWNER/REPO', 'commits-0-commit_hash': '12345678', } resp = client.post('/CVE-1970-1000/create', data=data) assert resp.status_code == expected_status if use_group == as_admin: assert resp.headers.get('Location', '<empty>').endswith(f'/1') vuln = Vulnerability.get_by_id(1) assert vuln.cve_id == data['cve_id'] assert len(vuln.commits) == 1 if use_group == as_admin: assert vuln.comment == data['comment'] assert vuln.commits[0].commit_link == data['commits-0-commit_link'] assert vuln.commits[0].repo_name == data['commits-0-repo_name'] assert vuln.commits[0].repo_url == data['commits-0-repo_url'] assert vuln.commits[0].commit_hash == data['commits-0-commit_hash'] else: assert vuln.comment == 'Vulnerability 1 comment' assert vuln.commits[ 0].commit_link == 'https://github.com/OWNER/REPO1/commit/1234568' assert vuln.commits[0].repo_name == 'REPO1' assert vuln.commits[0].repo_url == 'https://github.com/OWNER/REPO1' assert vuln.commits[0].commit_hash == '1234568'
def test_update_vulnerability(client, db_session, use_group, expected_status): if use_group: use_group(client) data = { "cve_id": "CVE-1970-1000", "comment": "This is the new comment", "commits-0-commit_link": "https://github.com/OWNER/REPO/commit/12345678", "commits-0-repo_name": "REPO", "commits-0-repo_url": "https://github.com/OWNER/REPO", "commits-0-commit_hash": "12345678", } resp = client.post("/CVE-1970-1000/create", data=data) assert resp.status_code == expected_status if use_group == as_admin: assert resp.headers.get("Location", "<empty>").endswith(f"/1") vuln = Vulnerability.get_by_id(1) assert vuln.cve_id == data["cve_id"] assert len(vuln.commits) == 1 if use_group == as_admin: assert vuln.comment == data["comment"] assert vuln.commits[0].commit_link == data["commits-0-commit_link"] assert vuln.commits[0].repo_name == data["commits-0-repo_name"] assert vuln.commits[0].repo_url == data["commits-0-repo_url"] assert vuln.commits[0].commit_hash == data["commits-0-commit_hash"] else: assert vuln.comment == "Vulnerability 1 comment" assert (vuln.commits[0].commit_link == "https://github.com/OWNER/REPO1/commit/1234568") assert vuln.commits[0].repo_name == "REPO1" assert vuln.commits[0].repo_url == "https://github.com/OWNER/REPO1" assert vuln.commits[0].commit_hash == "1234568"
def test_delete_vulnerability_entry(client, use_group, expected_status, expected_deletion): if use_group: use_group(client) vuln = Vulnerability.get_by_cve_id('CVE-1970-1000') assert vuln is not None resp = client.post('/CVE-1970-1000/create', data={ 'delete_entry': vuln.id, }) assert resp.status_code == expected_status vuln = Vulnerability.get_by_cve_id('CVE-1970-1000') if expected_deletion: assert vuln is None else: assert vuln is not None
def test_state_matrix(src_state, dst_state, exc): cr1 = creator1() vuln = Vulnerability(state=src_state, creator=cr1) if exc: with pytest.raises(exc): vuln.update_state(dst_state) else: vuln.update_state(dst_state)
def test_deny_reviewed(): vuln = Vulnerability(state=VulnerabilityState.REVIEWED) vuln.deny_change('merge conflict') assert vuln.state == VulnerabilityState.NEW assert vuln.review_feedback == 'merge conflict' assert not vuln.is_publishable() assert not vuln.is_reviewable()
def test_update_vulnerabilty(client, db_session): data = { 'cve_id': 'CVE-1970-1000', 'comment': 'This is the new comment', 'commits-0-commit_link': 'https://github.com/OWNER/REPO/commit/12345678', 'commits-0-repo_name': 'REPO', 'commits-0-repo_url': 'https://github.com/OWNER/REPO', 'commits-0-commit_hash': '12345678', } resp = client.post('/CVE-1970-1000/create', data=data) assert resp.status_code == 401 vuln = Vulnerability.get_by_id(1) assert vuln.comment == 'Vulnerability 1 comment' assert vuln.cve_id == data['cve_id'] assert len(vuln.commits) == 1 assert vuln.commits[ 0].commit_link == 'https://github.com/OWNER/REPO1/commit/1234568' assert vuln.commits[0].repo_name == 'REPO1' assert vuln.commits[0].repo_url == 'https://github.com/OWNER/REPO1' assert vuln.commits[0].commit_hash == '1234568'
def test_update_vulnerabilty_as_admin(client, db_session): data = { 'cve_id': 'CVE-1970-1000', 'comment': 'This is the new comment', 'commits-0-commit_link': 'https://github.com/OWNER/REPO/commit/12345678', 'commits-0-repo_name': 'REPO', 'commits-0-repo_url': 'https://github.com/OWNER/REPO', 'commits-0-commit_hash': '12345678', } as_admin(client) resp = client.post('/CVE-1970-1000/create', data=data) assert resp.status_code == 302 assert resp.headers.get('Location', '<empty>').endswith(f'/1') vuln = Vulnerability.get_by_id(1) assert vuln.comment == data['comment'] assert vuln.cve_id == data['cve_id'] assert len(vuln.commits) == 1 assert vuln.commits[0].commit_link == data['commits-0-commit_link'] assert vuln.commits[0].repo_name == data['commits-0-repo_name'] assert vuln.commits[0].repo_url == data['commits-0-repo_url'] assert vuln.commits[0].commit_hash == data['commits-0-commit_hash']
def has_vcdb_entry(self): return Vulnerability.get_by_cve_id(self.cve_id)
def setup_test_database(): """Returns session-wide initialised database.""" # Create a temporary flask app for the database setup. # We don't use the app or db fixtures here as they should be # executed in the function scope, not in the session scope like # this function is. app = create_app(TEST_CONFIG) with app.app_context(): db: SQLAlchemy = app.extensions["sqlalchemy"].db # setup databases and tables with open(os.path.join(cfg.BASE_DIR, "docker/db_schema.sql"), "rb") as f: create_schemas_sql = f.read().decode("utf8") # with app.app_context(): # clear database db.drop_all() db.engine.execute("DROP TABLE IF EXISTS alembic_version") # build database db.engine.execute(create_schemas_sql) alembic_upgrade() # create data session = db.session roles = [ Role(name=role) for role in (PredefinedRoles.ADMIN, PredefinedRoles.USER) ] session.add_all(roles) users = [ User( login="******", full_name="Admin McAdmin", roles=roles, state=UserState.ACTIVE, login_type=LoginType.LOCAL, ), User( login="******", full_name="User McUser", roles=[roles[1]], state=UserState.ACTIVE, login_type=LoginType.LOCAL, ), User( login="******", full_name="Blocked User", roles=[roles[1]], state=UserState.BLOCKED, login_type=LoginType.LOCAL, ), ] session.add_all(users) vuln_cves = list("CVE-1970-{}".format(1000 + i) for i in range(10)) new_cves = list("CVE-1970-{}".format(2000 + i) for i in range(10)) cves = vuln_cves + new_cves nvds = [] for i, cve in enumerate(cves, 1): nvds.append( Nvd( cve_id=cve, descriptions=[ Description( value="Description {}".format(i), ), ], references=[ Reference( link="https://cve.mitre.org/cgi-bin/cvename.cgi?name={}".format( cve ), source="cve.mitre.org", ), ], published_date=datetime.date.today(), cpes=[ Cpe( vendor="Vendor {}".format(i), product="Product {}".format(j), ) for j in range(1, 4) ], ) ) session.add_all(nvds) vulns = [] for i, cve in enumerate(vuln_cves, 1): repo_owner = "OWNER" repo_name = "REPO{i}".format(i=i) repo_url = "https://github.com/{owner}/{repo}/".format( owner=repo_owner, repo=repo_name, ) commit = "{:07x}".format(0x1234567 + i) vulns.append( Vulnerability( vcdb_id=i, cve_id=cve, date_created=datetime.date.today(), creator=users[1], state=VulnerabilityState.PUBLISHED, version=0, comment="Vulnerability {} comment".format(i), commits=[ VulnerabilityGitCommits( commit_link="{repo_url}commit/{commit}".format( repo_url=repo_url, commit=commit, ), repo_owner=repo_owner, repo_name=repo_name, # TODO: test conflicting data? repo_url=repo_url, commit_hash=commit, ) ], ) ) vulns.append( Vulnerability( state=VulnerabilityState.PUBLISHED, version=0, vcdb_id=len(vulns) + 1, cve_id="CVE-1970-1500", date_created=datetime.date.today(), comment="Vulnerability {} comment".format(len(vuln_cves) + 1), commits=[], ) ) session.add_all(vulns) session.commit()
def _db(app): """Returns session-wide initialised database.""" db = DEFAULT_DATABASE.db # setup databases and tables with open(os.path.join(cfg.BASE_DIR, 'docker/db_schema.sql'), 'rb') as f: create_schemas_sql = f.read().decode('utf8') with app.app_context(): # clear database db.drop_all() db.engine.execute('DROP TABLE IF EXISTS alembic_version') # build database db.engine.execute(create_schemas_sql) alembic_upgrade() # create data vuln_cves = list('CVE-1970-{}'.format(1000 + i) for i in range(10)) new_cves = list('CVE-1970-{}'.format(2000 + i) for i in range(10)) cves = vuln_cves + new_cves session = db.session nvds = [] for i, cve in enumerate(cves, 1): nvds.append( Nvd(cve_id=cve, descriptions=[ Description(value='Description {}'.format(i), ), ], references=[ Reference( link= 'https://cve.mitre.org/cgi-bin/cvename.cgi?name={}' .format(cve), source='cve.mitre.org', ), ], published_date=datetime.date.today(), cpes=[ Cpe( vendor='Vendor {}'.format(i), product='Product {}'.format(j), ) for j in range(1, 4) ])) session.add_all(nvds) vulns = [] for i, cve in enumerate(vuln_cves, 1): repo_owner = 'OWNER' repo_name = 'REPO{i}'.format(i=i) repo_url = 'https://github.com/{owner}/{repo}/'.format( owner=repo_owner, repo=repo_name, ) commit = '{:07x}'.format(0x1234567 + i) vulns.append( Vulnerability( cve_id=cve, date_created=datetime.date.today(), comment='Vulnerability {} comment'.format(i), commits=[ VulnerabilityGitCommits( commit_link='{repo_url}commit/{commit}'.format( repo_url=repo_url, commit=commit, ), repo_owner=repo_owner, repo_name=repo_name, # repo_url=repo_url, commit_hash=commit) ])) vulns.append( Vulnerability( cve_id='CVE-1970-1500', date_created=datetime.date.today(), comment='Vulnerability {} comment'.format(len(vuln_cves) + 1), commits=[])) session.add_all(vulns) users = [ User( email='*****@*****.**', full_name='Admin McAdmin', ), User( email='*****@*****.**', full_name='User McUser', ) ] session.add_all(users) session.commit() return db
def test_accept_change_after_review(): vuln = Vulnerability(state=VulnerabilityState.IN_REVIEW) vuln.accept_change() assert vuln.state == VulnerabilityState.REVIEWED assert vuln.is_publishable()