Skip to content

Commit 55dc5d3

Browse files
authored
Migrate advisory todo to v2 (#1966)
* Migrate Advisory ToDo pipeline to V2 Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com> * Remove print statements Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com> * Add compute advisory todo V2 to improver registry Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com> * Remove unused imports Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com> * Fix formatting issues Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com> * Address review comments Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com> --------- Signed-off-by: Tushar Goel <tushar.goel.dav@gmail.com>
1 parent 5121e31 commit 55dc5d3

File tree

6 files changed

+773
-1
lines changed

6 files changed

+773
-1
lines changed

vulnerabilities/improvers/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from vulnerabilities.pipelines import flag_ghost_packages
2020
from vulnerabilities.pipelines import populate_vulnerability_summary_pipeline
2121
from vulnerabilities.pipelines import remove_duplicate_advisories
22+
from vulnerabilities.pipelines.v2_improvers import compute_advisory_todo as compute_advisory_todo_v2
2223
from vulnerabilities.pipelines.v2_improvers import compute_package_risk as compute_package_risk_v2
2324
from vulnerabilities.pipelines.v2_improvers import (
2425
computer_package_version_rank as compute_version_rank_v2,
@@ -65,6 +66,7 @@
6566
enhance_with_metasploit_v2.MetasploitImproverPipeline,
6667
compute_package_risk_v2.ComputePackageRiskPipeline,
6768
compute_version_rank_v2.ComputeVersionRankPipeline,
69+
compute_advisory_todo_v2.ComputeToDo,
6870
compute_advisory_todo.ComputeToDo,
6971
]
7072
)
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# Generated by Django 4.2.22 on 2025-07-24 12:05
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
("vulnerabilities", "0100_remove_advisoryv2_affecting_packages_and_more"),
11+
]
12+
13+
operations = [
14+
migrations.CreateModel(
15+
name="AdvisoryToDoV2",
16+
fields=[
17+
(
18+
"id",
19+
models.AutoField(
20+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
21+
),
22+
),
23+
(
24+
"related_advisories_id",
25+
models.CharField(
26+
help_text="SHA1 digest of the unique_content_id field of the applicable advisories.",
27+
max_length=40,
28+
),
29+
),
30+
(
31+
"issue_type",
32+
models.CharField(
33+
choices=[
34+
("MISSING_AFFECTED_PACKAGE", "Advisory is missing affected package"),
35+
("MISSING_FIXED_BY_PACKAGE", "Advisory is missing fixed-by package"),
36+
(
37+
"MISSING_AFFECTED_AND_FIXED_BY_PACKAGES",
38+
"Advisory is missing both affected and fixed-by packages",
39+
),
40+
("MISSING_SUMMARY", "Advisory is missing summary"),
41+
(
42+
"CONFLICTING_FIXED_BY_PACKAGES",
43+
"Advisories have conflicting fixed-by packages",
44+
),
45+
(
46+
"CONFLICTING_AFFECTED_PACKAGES",
47+
"Advisories have conflicting affected packages",
48+
),
49+
(
50+
"CONFLICTING_AFFECTED_AND_FIXED_BY_PACKAGES",
51+
"Advisories have conflicting affected and fixed-by packages",
52+
),
53+
(
54+
"CONFLICTING_SEVERITY_SCORES",
55+
"Advisories have conflicting severity scores",
56+
),
57+
],
58+
db_index=True,
59+
help_text="Select the issue that needs to be addressed from the available options.",
60+
max_length=50,
61+
),
62+
),
63+
(
64+
"issue_detail",
65+
models.TextField(blank=True, help_text="Additional details about the issue."),
66+
),
67+
(
68+
"created_at",
69+
models.DateTimeField(
70+
auto_now_add=True,
71+
help_text="Timestamp indicating when this TODO was created.",
72+
),
73+
),
74+
(
75+
"is_resolved",
76+
models.BooleanField(
77+
db_index=True, default=False, help_text="This TODO is resolved or not."
78+
),
79+
),
80+
(
81+
"resolved_at",
82+
models.DateTimeField(
83+
blank=True,
84+
help_text="Timestamp indicating when this TODO was resolved.",
85+
null=True,
86+
),
87+
),
88+
(
89+
"resolution_detail",
90+
models.TextField(
91+
blank=True, help_text="Additional detail on how this TODO was resolved."
92+
),
93+
),
94+
],
95+
),
96+
migrations.CreateModel(
97+
name="ToDoRelatedAdvisoryV2",
98+
fields=[
99+
(
100+
"id",
101+
models.AutoField(
102+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
103+
),
104+
),
105+
(
106+
"advisory",
107+
models.ForeignKey(
108+
on_delete=django.db.models.deletion.CASCADE, to="vulnerabilities.advisoryv2"
109+
),
110+
),
111+
(
112+
"todo",
113+
models.ForeignKey(
114+
on_delete=django.db.models.deletion.CASCADE,
115+
to="vulnerabilities.advisorytodov2",
116+
),
117+
),
118+
],
119+
options={
120+
"unique_together": {("todo", "advisory")},
121+
},
122+
),
123+
migrations.AddField(
124+
model_name="advisorytodov2",
125+
name="advisories",
126+
field=models.ManyToManyField(
127+
help_text="Advisory/ies where this TODO is applicable.",
128+
related_name="advisory_todos",
129+
through="vulnerabilities.ToDoRelatedAdvisoryV2",
130+
to="vulnerabilities.advisoryv2",
131+
),
132+
),
133+
migrations.AlterUniqueTogether(
134+
name="advisorytodov2",
135+
unique_together={("related_advisories_id", "issue_type")},
136+
),
137+
]

vulnerabilities/models.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2493,6 +2493,62 @@ class Meta:
24932493
unique_together = ("related_advisories_id", "issue_type")
24942494

24952495

2496+
class AdvisoryToDoV2(models.Model):
2497+
"""Track the TODOs for advisory/ies that need to be addressed."""
2498+
2499+
# Since we can not make advisories field (M2M field) unique
2500+
# (see https://code.djangoproject.com/ticket/702), we use related_advisories_id
2501+
# to avoid creating duplicate issue for same set of advisories,
2502+
related_advisories_id = models.CharField(
2503+
max_length=40,
2504+
help_text="SHA1 digest of the unique_content_id field of the applicable advisories.",
2505+
)
2506+
2507+
advisories = models.ManyToManyField(
2508+
"AdvisoryV2",
2509+
through="ToDoRelatedAdvisoryV2",
2510+
related_name="advisory_todos",
2511+
help_text="Advisory/ies where this TODO is applicable.",
2512+
)
2513+
2514+
issue_type = models.CharField(
2515+
max_length=50,
2516+
choices=ISSUE_TYPE_CHOICES,
2517+
db_index=True,
2518+
help_text="Select the issue that needs to be addressed from the available options.",
2519+
)
2520+
2521+
issue_detail = models.TextField(
2522+
blank=True,
2523+
help_text="Additional details about the issue.",
2524+
)
2525+
2526+
created_at = models.DateTimeField(
2527+
auto_now_add=True,
2528+
help_text="Timestamp indicating when this TODO was created.",
2529+
)
2530+
2531+
is_resolved = models.BooleanField(
2532+
default=False,
2533+
db_index=True,
2534+
help_text="This TODO is resolved or not.",
2535+
)
2536+
2537+
resolved_at = models.DateTimeField(
2538+
null=True,
2539+
blank=True,
2540+
help_text="Timestamp indicating when this TODO was resolved.",
2541+
)
2542+
2543+
resolution_detail = models.TextField(
2544+
blank=True,
2545+
help_text="Additional detail on how this TODO was resolved.",
2546+
)
2547+
2548+
class Meta:
2549+
unique_together = ("related_advisories_id", "issue_type")
2550+
2551+
24962552
class AdvisorySeverity(models.Model):
24972553
url = models.URLField(
24982554
max_length=1024,
@@ -2933,6 +2989,21 @@ class Meta:
29332989
unique_together = ("todo", "advisory")
29342990

29352991

2992+
class ToDoRelatedAdvisoryV2(models.Model):
2993+
todo = models.ForeignKey(
2994+
AdvisoryToDoV2,
2995+
on_delete=models.CASCADE,
2996+
)
2997+
2998+
advisory = models.ForeignKey(
2999+
AdvisoryV2,
3000+
on_delete=models.CASCADE,
3001+
)
3002+
3003+
class Meta:
3004+
unique_together = ("todo", "advisory")
3005+
3006+
29363007
class PackageQuerySetV2(BaseQuerySet, PackageURLQuerySet):
29373008
def search(self, query: str = None):
29383009
"""

0 commit comments

Comments
 (0)