diff --git a/apps/osim/tests/test_endpoints.py b/apps/osim/tests/test_endpoints.py index 0a5b4a263..5dba394ff 100644 --- a/apps/osim/tests/test_endpoints.py +++ b/apps/osim/tests/test_endpoints.py @@ -94,13 +94,13 @@ def test_workflows_uuid_adjusting(self, auth_client, test_api_uri): workflow_framework = WorkflowFramework() state_new = State( { - "name": WorkflowModel.OSIMState.DRAFT, + "name": WorkflowModel.OSIMState.NEW, "requirements": [], } ) state_first = State( { - "name": WorkflowModel.OSIMState.ANALYSIS, + "name": WorkflowModel.OSIMState.TRIAGE, "requirements": ["has description"], } ) diff --git a/apps/osim/tests/test_integration.py b/apps/osim/tests/test_integration.py index 0b48016e4..2463bafde 100644 --- a/apps/osim/tests/test_integration.py +++ b/apps/osim/tests/test_integration.py @@ -220,11 +220,11 @@ def test_healthy(self, auth_client, test_scheme_host): def test_adjust(self, auth_client, test_api_uri): """test refreshing/adjusting a flaw state through API""" state_new = { - "name": WorkflowModel.OSIMState.DRAFT, + "name": WorkflowModel.OSIMState.NEW, "requirements": [], } state_first = { - "name": WorkflowModel.OSIMState.ANALYSIS, + "name": WorkflowModel.OSIMState.TRIAGE, "requirements": ["has cwe"], } @@ -246,7 +246,7 @@ def test_adjust(self, auth_client, test_api_uri): flaw.cwe_id = "" flaw.save(raise_validation_error=False) flaw.adjust_classification() - assert flaw.classification["state"] == WorkflowModel.OSIMState.DRAFT + assert flaw.classification["state"] == WorkflowModel.OSIMState.NEW flaw.cwe_id = "CWE-1" flaw.save(raise_validation_error=False) @@ -260,11 +260,11 @@ def test_adjust(self, auth_client, test_api_uri): json_body = response.json() assert str(flaw.uuid) == json_body["flaw"] - assert WorkflowModel.OSIMState.ANALYSIS == json_body["classification"]["state"] + assert WorkflowModel.OSIMState.TRIAGE == json_body["classification"]["state"] def test_classification(self, auth_client, test_api_uri): state_new = { - "name": WorkflowModel.OSIMState.DRAFT, + "name": WorkflowModel.OSIMState.NEW, "requirements": [], } @@ -295,7 +295,7 @@ def test_classification(self, auth_client, test_api_uri): assert "state" in json_body["classification"] assert json_body["classification"]["workflow"] == "main workflow" - assert json_body["classification"]["state"] == WorkflowModel.OSIMState.DRAFT + assert json_body["classification"]["state"] == WorkflowModel.OSIMState.NEW def test_workflows(self, auth_client, test_api_uri): response = auth_client.get( diff --git a/apps/osim/tests/test_models.py b/apps/osim/tests/test_models.py index 173723892..a3be5e74d 100644 --- a/apps/osim/tests/test_models.py +++ b/apps/osim/tests/test_models.py @@ -400,11 +400,11 @@ def test_classify_priority(self): def test_classify_complete(self): """test flaw classification in both workflow and state""" state_new = { - "name": WorkflowModel.OSIMState.DRAFT, + "name": WorkflowModel.OSIMState.NEW, "requirements": [], } state_first = { - "name": WorkflowModel.OSIMState.ANALYSIS, + "name": WorkflowModel.OSIMState.TRIAGE, "requirements": ["has description"], } state_second = { @@ -423,7 +423,7 @@ def test_classify_complete(self): ) state_not_affected = { - "name": WorkflowModel.OSIMState.REVIEW, + "name": WorkflowModel.OSIMState.REJECTED, "requirements": [], } @@ -538,6 +538,50 @@ def test_init(self): def test_classification(self): """test flaw classification property""" + state_new = { + "name": WorkflowModel.OSIMState.NEW, + "requirements": [], + } + state_first = { + "name": WorkflowModel.OSIMState.TRIAGE, + "requirements": ["has description"], + } + state_second = { + "name": WorkflowModel.OSIMState.DONE, + "requirements": ["has title"], + } + + workflow_main = Workflow( + { + "name": "main workflow", + "description": "a three step workflow to test classification", + "priority": 0, + "conditions": [], + "states": [state_new, state_first, state_second], + } + ) + + state_not_affected = { + "name": WorkflowModel.OSIMState.REJECTED, + "requirements": [], + } + + workflow_reject = Workflow( + { + "name": "reject workflow", + "description": "a worflow for rejected flaws", + "priority": 1, + "conditions": ["has affects", "affects notaffected"], + "states": [state_not_affected], + } + ) + + workflow_framework = WorkflowFramework() + # remove yml workflows + workflow_framework._workflows = [] + workflow_framework.register_workflow(workflow_main) + workflow_framework.register_workflow(workflow_reject) + flaw = Flaw() # stored classification @@ -548,7 +592,6 @@ def test_classification(self): old_computed_state = flaw.classify()["state"] assert flaw.osim_workflow == old_computed_workflow assert flaw.osim_state == old_computed_state - # assing new and different classification for workflow in WorkflowFramework().workflows: if workflow.name != flaw.osim_workflow: @@ -583,13 +626,13 @@ def test_adjust(self): state_new = State( { - "name": WorkflowModel.OSIMState.DRAFT, + "name": WorkflowModel.OSIMState.NEW, "requirements": [], } ) state_first = State( { - "name": WorkflowModel.OSIMState.ANALYSIS, + "name": WorkflowModel.OSIMState.TRIAGE, "requirements": ["has description"], } ) diff --git a/apps/osim/workflow.py b/apps/osim/workflow.py index f7b5fe80c..d2eb4024f 100644 --- a/apps/osim/workflow.py +++ b/apps/osim/workflow.py @@ -107,16 +107,16 @@ class WorkflowModel(models.Model): class OSIMState(models.TextChoices): """allowable workflow states""" - DRAFT = "DRAFT" NEW = "NEW" - ANALYSIS = "ANALYSIS" - REVIEW = "REVIEW" - FIX = "FIX" + TRIAGE = "TRIAGE" + PRE_SECOND_ASSESSMENT = "PRE_SECONDARY_ASSESSMENT" + SECOND_ASSESSMENT = "SECONDARY_ASSESSMENT" DONE = "DONE" + REJECTED = "REJECTED" # workflow metadata osim_workflow = models.CharField(max_length=50, blank=True) - osim_state = models.CharField(choices=OSIMState.choices, max_length=10, blank=True) + osim_state = models.CharField(choices=OSIMState.choices, max_length=24, blank=True) class Meta: abstract = True diff --git a/apps/osim/workflows/default.yml b/apps/osim/workflows/default.yml index 23e7e202e..bc569aeee 100644 --- a/apps/osim/workflows/default.yml +++ b/apps/osim/workflows/default.yml @@ -1,34 +1,66 @@ --- name: DEFAULT -description: default vulnerability analysis +description: default vulnerability workflow. priority: 0 conditions: [] +enter_automatically: true states: - - name: DRAFT + - name: NEW + description: > + A new ticket arrived from incoming queue without any validation + or analysis performed yet. + jira_state: New + jira_resolution: null + enter_automatically: true requirements: [] - - name: ANALYSIS + - name: TRIAGE + description: > + Queue captain has taken a ticket off the incoming queue and will + evalute if it should be rejected or qualified for further steps in + the workflow. + jira_state: Refinement + jira_resolution: null + enter_automatically: true + requirements: + - has owner + + - name: PRE_SECONDARY_ASSESSMENT + description: > + Task qualified for further work and has initial data filled, triage + trackers have been filled and product team has been identified. + jira_state: To Do + jira_resolution: null + enter_automatically: true requirements: - has affects - - has description + - has impact - has source + - has team - has title + - has trackers - - name: REVIEW - requirements: - - affects resolved - - has cve - - has cwe - - - name: FIX + - name: SECONDARY_ASSESSMENT + description: An analyst assigned the task to themselves. + jira_state: In Progress + jira_resolution: null + enter_automatically: true requirements: - - trackers filed + - has owner - # you can freely comment as you wish - # maybe about that this is the final state - name: DONE - requirements: - # TODO embargoed is not model attribute any more but annotation - # so this currently errors out and we need to accout for the change - #- not embargoed - - trackers resolved + description: > + Task is done. We commonly expect that a CVE will be + published, but it is not a requirement. + jira_state: Closed + jira_resolution: Done + enter_automatically: false + requirements: [] + + - name: REJECTED + description: > + Task were rejected either by the queue captain or the analyst. + jira_state: Closed + jira_resolution: Won't Do + enter_automatically: false + requirements: [] diff --git a/apps/osim/workflows/not-affected.yml b/apps/osim/workflows/not-affected.yml deleted file mode 100644 index db191c7d3..000000000 --- a/apps/osim/workflows/not-affected.yml +++ /dev/null @@ -1,25 +0,0 @@ ---- -name: NOT-AFFECTED -description: > - vulnerability does not affect any Red Hat or community products - the flaw was analysed but all affects were resolved as notaffected - there is no followup action required after resolving the affects -priority: 1 -conditions: - - has affects - - affects resolved - - affects notaffected -states: - - name: DRAFT - requirements: [] - - - name: ANALYSIS - requirements: - - has affects - - has description - - has source - - has title - - - name: DONE - requirements: - - affects resolved diff --git a/apps/taskman/migrations/0001_initial.py b/apps/taskman/migrations/0001_initial.py index fca952b2e..2e416e9dc 100644 --- a/apps/taskman/migrations/0001_initial.py +++ b/apps/taskman/migrations/0001_initial.py @@ -3,10 +3,7 @@ from django.db import migrations, models import django.db.models.deletion import uuid - - class Migration(migrations.Migration): - initial = True dependencies = [ diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 2ceceb899..e4afabc30 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -36,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Deprecate various cvss fields in Flaw and Affect APIs (OSIDB-1105) - Update CORS policy to allow bugzilla-api-key request header (OSIDB-1425) +- Change workflows to reflect current IR workflow (OSIDB-1319) ### Fixed - Fix schema wrongly showing status code for DELETE methods being 204 diff --git a/openapi.yml b/openapi.yml index 9b9a9766f..6094023d2 100644 --- a/openapi.yml +++ b/openapi.yml @@ -8013,12 +8013,12 @@ components: state: type: string enum: - - DRAFT - NEW - - ANALYSIS - - REVIEW - - FIX + - TRIAGE + - PRE_SECONDARY_ASSESSMENT + - SECONDARY_ASSESSMENT - DONE + - REJECTED readOnly: true required: - acknowledgments @@ -8689,12 +8689,12 @@ components: state: type: string enum: - - DRAFT - NEW - - ANALYSIS - - REVIEW - - FIX + - TRIAGE + - PRE_SECONDARY_ASSESSMENT + - SECONDARY_ASSESSMENT - DONE + - REJECTED readOnly: true required: - acknowledgments diff --git a/osidb/migrations/0101_alter_flaw_osim_state.py b/osidb/migrations/0101_alter_flaw_osim_state.py new file mode 100644 index 000000000..80b1d96ac --- /dev/null +++ b/osidb/migrations/0101_alter_flaw_osim_state.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.20 on 2023-10-24 00:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('osidb', '0100_fix_affectcvss'), + ] + + operations = [ + migrations.AlterField( + model_name='flaw', + name='osim_state', + field=models.CharField(blank=True, choices=[('NEW', 'New'), ('TRIAGE', 'Triage'), ('PRE_SECONDARY_ASSESSMENT', 'Pre Second Assessment'), ('SECONDARY_ASSESSMENT', 'Second Assessment'), ('DONE', 'Done'), ('REJECTED', 'Rejected')], max_length=24), + ), + ] diff --git a/osidb/models.py b/osidb/models.py index 41f0d0965..a4efa374b 100644 --- a/osidb/models.py +++ b/osidb/models.py @@ -1422,6 +1422,16 @@ def trackers_filed(self): for affect in self.affects.filter(resolution=Affect.AffectResolution.FIX) ) + @property + def task_owner(self): + """return the current task owner in Jira""" + return self.task.owner if hasattr(self, "task") else "" + + @property + def task_team_id(self): + """return the current team id in Jira""" + return self.task.team_id if hasattr(self, "task") else "" + @property def trackers_resolved(self): """check that all trackers have resolution"""