Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Generated by Django 4.2.22 on 2025-10-22 11:56

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("vulnerabilities", "0102_alter_impactedpackage_affecting_vers_and_more"),
]

operations = [
migrations.RemoveField(
model_name="codefixv2",
name="affected_package",
),
migrations.RemoveField(
model_name="codefixv2",
name="fixed_package",
),
migrations.AddField(
model_name="impactedpackage",
name="affected_by_commits",
field=models.ManyToManyField(
help_text="Commits that are affected by this impact.",
related_name="affected_commits_impacts",
to="vulnerabilities.codefixv2",
),
),
migrations.AddField(
model_name="impactedpackage",
name="fixed_by_commits",
field=models.ManyToManyField(
help_text="Commits that fix the vulnerability in this impact.",
related_name="fixed_commits_impacts",
to="vulnerabilities.codefixv2",
),
),
migrations.AlterField(
model_name="advisoryreference",
name="reference_type",
field=models.CharField(
blank=True,
choices=[
("advisory", "Advisory"),
("exploit", "Exploit"),
("commit", "Commit"),
("mailing_list", "Mailing List"),
("bug", "Bug"),
("other", "Other"),
],
max_length=20,
),
),
]
27 changes: 14 additions & 13 deletions vulnerabilities/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1960,19 +1960,6 @@ class CodeFixV2(CodeChangeV2):
help_text="The affected package version to which this code fix applies.",
)

affected_package = models.ForeignKey(
"PackageV2", on_delete=models.CASCADE, related_name="code_fix_v2_affected"
)

fixed_package = models.ForeignKey(
"PackageV2",
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name="code_fix_v2_fixed",
help_text="The fixing package version with this code fix",
)


class PipelineRun(models.Model):
"""The Database representation of a pipeline execution."""
Expand Down Expand Up @@ -2662,13 +2649,15 @@ class AdvisoryReference(models.Model):

ADVISORY = "advisory"
EXPLOIT = "exploit"
COMMIT = "commit"
MAILING_LIST = "mailing_list"
BUG = "bug"
OTHER = "other"

REFERENCE_TYPES = [
(ADVISORY, "Advisory"),
(EXPLOIT, "Exploit"),
(COMMIT, "Commit"),
(MAILING_LIST, "Mailing List"),
(BUG, "Bug"),
(OTHER, "Other"),
Expand Down Expand Up @@ -2957,6 +2946,18 @@ class ImpactedPackage(models.Model):
help_text="Packages vulnerable to this impact.",
)

fixed_by_commits = models.ManyToManyField(
"CodeFixV2",
related_name="fixed_commits_impacts",
help_text="Commits that fix the vulnerability in this impact.",
)

affected_by_commits = models.ManyToManyField(
"CodeFixV2",
related_name="affected_commits_impacts",
help_text="Commits that are affected by this impact.",
)

created_at = models.DateTimeField(
auto_now_add=True,
db_index=True,
Expand Down
17 changes: 8 additions & 9 deletions vulnerabilities/pipelines/v2_improvers/collect_commits.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,14 @@ def collect_and_store_fix_commits(self):
continue
# check if vcs_url has commit
for impact in adv.impacted_packages.all():
for package in impact.affecting_packages.all():
code_fix, created = CodeFixV2.objects.get_or_create(
commits=[vcs_url],
advisory=adv,
affected_package=package,
)

if created:
created_fix_count += 1
code_fix, created = CodeFixV2.objects.get_or_create(
commits=[vcs_url],
advisory=adv,
)
impact.fixed_by_commits.add(code_fix)

if created:
created_fix_count += 1

self.log(f"Successfully created {created_fix_count:,d} CodeFix entries.")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,19 +62,29 @@ def test_is_vcs_url_already_processed_true():
unique_content_id="11111",
date_collected=datetime.now(),
)

commit1 = CodeFixV2.objects.create(advisory=advisory)
commit1.commits = ["https://github.com/user/repo/commit/abc1"]
commit1.save()

commit2 = CodeFixV2.objects.create(advisory=advisory)
commit2.commits = ["https://github.com/user/repo/commit/abc2"]
commit2.save()

package = PackageV2.objects.create(
type="bar",
name="foo",
version="1.0",
)
impact = ImpactedPackage.objects.create(advisory=advisory)
impact.affecting_packages.add(package)
CodeFixV2.objects.create(
commits=["https://github.com/user/repo/commit/abc123"],

impact = ImpactedPackage.objects.create(
advisory=advisory,
affected_package=package,
)
assert is_vcs_url_already_processed("https://github.com/user/repo/commit/abc123") is True
impact.affecting_packages.add(package)
impact.fixed_by_commits.add(commit1)
impact.affected_by_commits.add(commit2)

assert is_vcs_url_already_processed("https://github.com/user/repo/commit/abc1") is True


@pytest.mark.django_db
Expand All @@ -93,7 +103,7 @@ def test_collect_fix_commits_pipeline_creates_entry():
version="1.0",
)
reference = AdvisoryReference.objects.create(
url="https://github.com/test/testpkg/commit/abc123"
reference_type="commit", url="https://github.com/test/testpkg/commit/abc123"
)
impact = ImpactedPackage.objects.create(advisory=advisory)
impact.affecting_packages.add(package)
Expand All @@ -107,7 +117,7 @@ def test_collect_fix_commits_pipeline_creates_entry():
fix = codefixes.first()
assert "abc123" in fix.commits[0]
assert fix.advisory == advisory
assert fix.affected_package == package
assert list(fix.fixed_commits_impacts.all()) == [impact]


@pytest.mark.django_db
Expand Down
9 changes: 2 additions & 7 deletions vulnerabilities/tests/test_api_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -798,13 +798,8 @@ def setUp(self):
date_collected="2025-07-01T00:00:00Z",
)

self.affected_package = PackageV2.objects.from_purl(purl="pkg:pypi/[email protected]")
self.fixed_package = PackageV2.objects.from_purl(purl="pkg:pypi/[email protected]")

self.codefix = CodeFixV2.objects.create(
advisory=self.advisory,
affected_package=self.affected_package,
fixed_package=self.fixed_package,
notes="Security patch",
is_reviewed=True,
)
Expand All @@ -816,14 +811,14 @@ def setUp(self):
self.url = reverse("advisory-codefix-list")

def test_list_all_codefixes(self):
with self.assertNumQueries(10):
with self.assertNumQueries(8):
response = self.client.get(self.url)
assert response.status_code == status.HTTP_200_OK
assert response.data["count"] == 1
assert response.data["results"][0]["affected_advisory_id"] == self.advisory.avid

def test_filter_codefix_by_advisory_id_success(self):
with self.assertNumQueries(10):
with self.assertNumQueries(8):
response = self.client.get(self.url, {"advisory_id": self.advisory.avid})
assert response.status_code == status.HTTP_200_OK
assert response.data["count"] == 1
Expand Down