diff --git a/test/conftest.py b/test/conftest.py index 3419d85..4b90133 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -63,22 +63,13 @@ def db(app, request): @pytest.fixture(autouse=True, scope='function') def run_scoped(app, db, client, request): with app.app_context(): - connection = db.engine.connect() - transaction = connection.begin() - - options = dict(bind=connection, binds={}) - session = db.create_scoped_session(options=options) - - db.session = session db.create_all() with client: yield + db.session.remove() db.drop_all() - transaction.rollback() - connection.close() - session.remove() @pytest.fixture(scope='function') diff --git a/test/test_group.py b/test/test_group.py index 7d62aa1..67667fb 100644 --- a/test/test_group.py +++ b/test/test_group.py @@ -1,3 +1,6 @@ +from urllib.parse import parse_qs +from urllib.parse import urlparse + from flask import url_for from werkzeug.exceptions import Forbidden from werkzeug.exceptions import NotFound @@ -11,7 +14,6 @@ from tracker.model.enum import UserRole from tracker.model.enum import affected_to_status from tracker.view.add import ERROR_GROUP_WITH_ISSUE_EXISTS -from tracker.view.show import get_bug_project from .conftest import DEFAULT_ADVISORY_ID from .conftest import DEFAULT_GROUP_ID @@ -59,10 +61,6 @@ def set_and_assert_group_data(db, client, route, pkgnames=['foo'], issues=['CVE- if bug_ticket: assert TRACKER_BUGTRACKER_URL.format(bug_ticket) in resp.data.decode('utf-8') - else: - # Assert project and product category - project = get_bug_project([database]) - assert 'project={}&product_category=13'.format(project) in resp.data.decode('utf-8') @create_package(name='foo') @@ -481,3 +479,46 @@ def test_edit_group_does_nothing_when_data_is_same(db, client): group = CVEGroup.query.get(DEFAULT_GROUP_ID) assert group.changed == group_changed_old + + +@create_package(name='foo', version='1.2.3-3') +@create_group(id=DEFAULT_GROUP_ID, issues=[DEFAULT_ISSUE_ID], packages=['foo'], status=Status.vulnerable) +@logged_in(role=UserRole.reporter) +def test_create_bug_ticket_redirect_for_vulnerable_group(db, client): + """ + Tests if clicking the 'Create' button for a vulnerable group correctly + redirects to a pre-filled GitLab 'New Issue' URL. + """ + resp = client.get(url_for('tracker.create_bug_ticket_redirect', avg=DEFAULT_GROUP_NAME), follow_redirects=False) + + assert 302 == resp.status_code + + location = resp.location + parsed_url = urlparse(location) + + assert 'gitlab.archlinux.org' == parsed_url.netloc + assert '/archlinux/packaging/packages/foo/-/issues/new' == parsed_url.path + + query_params = parse_qs(parsed_url.query) + assert 'issue[title]' in query_params + assert 'issue[description]' in query_params + + title = query_params['issue[title]'][0] + assert '[foo]' in title + assert '[Security]' in title + assert f'({DEFAULT_ISSUE_ID})' in title + + +@create_package(name='foo', version='1.2.3-4') +@create_group(id=DEFAULT_GROUP_ID, issues=[DEFAULT_ISSUE_ID], packages=['foo'], status=Status.fixed) +@logged_in(role=UserRole.reporter) +def test_create_bug_ticket_redirect_for_fixed_group_fails(db, client): + """ + Tests that attempting to create a bug for a non-vulnerable group + redirects back to the group page with a flash message. + """ + resp = client.get(url_for('tracker.create_bug_ticket_redirect', avg=DEFAULT_GROUP_NAME), follow_redirects=True) + + assert 200 == resp.status_code + assert f'

{DEFAULT_GROUP_NAME}' in resp.data.decode('utf-8') + assert 'Bug ticket can only be created for vulnerable groups.' in resp.data.decode('utf-8') diff --git a/tracker/templates/group.html b/tracker/templates/group.html index 6d7f16e..645e66d 100644 --- a/tracker/templates/group.html +++ b/tracker/templates/group.html @@ -53,7 +53,7 @@

{{ group.name }} {%- if group.bug_ticket %} {{ bug_ticket(group.bug_ticket) }} {%- elif group.status == "Vulnerable" %} - Create + Create {%- else %} None {%- endif %} diff --git a/tracker/view/show.py b/tracker/view/show.py index 0e89a4b..847eb6b 100644 --- a/tracker/view/show.py +++ b/tracker/view/show.py @@ -1,8 +1,10 @@ from collections import OrderedDict from collections import defaultdict +from flask import flash from flask import redirect from flask import render_template +from flask import url_for from flask_login import current_user from markupsafe import escape from sqlalchemy import and_ @@ -38,6 +40,7 @@ from tracker.model.enum import Status from tracker.model.package import filter_duplicate_packages from tracker.model.package import sort_packages +from tracker.user import reporter_required from tracker.user import user_can_delete_group from tracker.user import user_can_delete_issue from tracker.user import user_can_edit_group @@ -45,25 +48,12 @@ from tracker.user import user_can_handle_advisory from tracker.user import user_can_watch_log from tracker.user import user_can_watch_user_log +from tracker.util import add_params_to_uri from tracker.util import json_response from tracker.util import multiline_to_list from tracker.view.error import not_found -def get_bug_project(databases): - bug_project_mapping = { - 1: ['core', 'core-testing', 'extra', 'extra-testing'], - 5: ['multilib', 'multilib-testing'] - } - - for category, repos in bug_project_mapping.items(): - if all((database in repos for database in databases)): - return category - - # Fallback - return 1 - - def get_bug_data(cves, pkgs, versions, group): references = [] references = [ref for ref in multiline_to_list(group.reference) @@ -89,23 +79,8 @@ def get_bug_data(cves, pkgs, versions, group): if TRACKER_SUMMARY_LENGTH_MAX != 0 and len(summary) > TRACKER_SUMMARY_LENGTH_MAX: summary = "[{}] [Security] {} (Multiple CVE's)".format(pkg_str, group_type) - # 5: critical, 4: high, 3: medium, 2: low, 1: very low. - severitiy_mapping = { - 'unknown': 3, - 'critical': 5, - 'high': 4, - 'medium': 3, - 'low': 2, - } - - task_severity = severitiy_mapping.get(group.severity.name) - project = get_bug_project((pkg.database for pkg in versions)) - return { - 'project': project, - 'product_category': 13, # security 'item_summary': summary, - 'task_severity': task_severity, 'detailed_desc': bug_desc } @@ -329,7 +304,6 @@ def show_group(avg): versions=versions, Status=Status, issue_type=issue_type, - bug_data=get_bug_data(issues, packages, versions, group), advisories_pending=data['advisories_pending'], can_edit=user_can_edit_group(advisories), can_delete=user_can_delete_group(advisories), @@ -592,3 +566,40 @@ def show_log(page=1): CVE=CVE, CVEGroup=CVEGroup, Advisory=Advisory) + + +@tracker.route('/group//create_bug'.format(vulnerability_group_regex[1:-1]), methods=['GET']) +@tracker.route('/avg//create_bug'.format(vulnerability_group_regex[1:-1]), methods=['GET']) +@tracker.route('//create_bug'.format(vulnerability_group_regex[1:-1]), methods=['GET']) +@reporter_required +def create_bug_ticket_redirect(avg): + data = get_group_data(avg) + if not data: + return not_found() + + group = data['group'] + issues = data['issues'] + packages = data['packages'] + versions = data['versions'] + + if group.status != Status.vulnerable: + flash('Bug ticket can only be created for vulnerable groups.', 'warning') + return redirect(url_for('tracker.show_group', avg=avg)) + + pkg_identifier = '' + if not versions: + pkg_identifier = packages[0].pkgname + else: + pkg_identifier = versions[0].base + + pkgname = packages[0].pkgname + gitlab_url = f"https://gitlab.archlinux.org/archlinux/packaging/packages/{pkgname}/-/issues/new" + + bug_data = get_bug_data(issues, packages, versions, group) + params = { + 'issue[title]': bug_data['item_summary'], + 'issue[description]': bug_data['detailed_desc'] + } + + final_url = add_params_to_uri(gitlab_url, params) + return redirect(final_url)