diff --git a/src/psrt_ghsa_bot/app.py b/src/psrt_ghsa_bot/app.py index 60da65b..851d5e1 100644 --- a/src/psrt_ghsa_bot/app.py +++ b/src/psrt_ghsa_bot/app.py @@ -4,6 +4,7 @@ import csv import datetime import os +import re import typing import urllib3 @@ -101,6 +102,19 @@ def apply_to_repo( print(f" 📋 Processing {ghsa_id} (state: {state})") + # If the summary contains '[CLOSE]', '[CLOSED]', '[COMPLETE]', + # or '[COMPLETED]' then we can close the ticket. + summary = security_advisory.get("summary", "") + if re.search(r"\[(?:CLOSED?|COMPLETED?)\]", summary.upper()) is not None: + github.rest.security_advisories.update_repository_advisory( + owner=owner, + repo=repo, + ghsa_id=ghsa_id, + data={"state": "closed"}, + ) + print(f" 📋 Closed {ghsa_id}") + continue + # Maintain a dictionary of updates to make and then submit them all at once. patch_data = {} diff --git a/tests/test_app.py b/tests/test_app.py index ac06c49..bb1d124 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -34,11 +34,12 @@ def cve_reserve_response(cve_id, year): } -def _create_advisory_dict(state, cve_id, collaborating_teams): +def _create_advisory_dict(state, cve_id, collaborating_teams, summary=""): """Helper to create a security advisory dictionary.""" return { "ghsa_id": "GHSA-xxxx-xxxx-xxxx", "state": state, + "summary": summary, "cve_id": cve_id, "collaborating_teams": [{"slug": team} for team in collaborating_teams], "collaborating_users": [{"login": "octocat", "id": 1, "type": "User"}], @@ -177,6 +178,35 @@ def test_update_collaborating_users() -> None: ) +@pytest.mark.parametrize( + "summary", + [ + "[CLOSE] perl is better than Python", + "[CLOSED] 0.1 + 0.2 is broken?!?!?!?!?!", + "[COMPLETE] some boring security thing", + "fix soemthing in datetime module [COMPLETED]", + "blah blah [closed] lowercase blah", + ], +) +def test_closes_advisory_with_close_or_complete_tag(summary) -> None: + security_advisory = _create_advisory_dict("triage", None, [], summary=summary) + + github = mock.Mock() + cve_api = mock.Mock() + + with mock.patch("psrt_ghsa_bot.app.get_repository_advisories") as get_repo_advs: + get_repo_advs.return_value = [security_advisory] + + app.apply_to_repo(github, "owner", "repo", cve_api) + + github.rest.security_advisories.update_repository_advisory.assert_called_once_with( + owner="owner", + repo="repo", + ghsa_id="GHSA-xxxx-xxxx-xxxx", + data={"state": "closed"}, + ) + + def test_load_psrt_members_from_devguide() -> None: with mock.patch("psrt_ghsa_bot.app.urllib3.request") as urllib3_request: resp = mock.Mock()