Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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,84 @@
# Generated by Django 4.2.22 on 2025-10-31 14:49

from django.db import migrations, models


class Migration(migrations.Migration):

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

operations = [
migrations.CreateModel(
name="CodeCommit",
fields=[
(
"id",
models.AutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
(
"commit_hash",
models.CharField(
help_text="Unique commit identifier (e.g., SHA).", max_length=64
),
),
(
"vcs_url",
models.URLField(
help_text="URL of the repository containing the commit.", max_length=1024
),
),
(
"commit_rank",
models.IntegerField(
default=0,
help_text="Rank of the commit to support ordering by commit. Rank zero means the rank has not been defined yet",
),
),
(
"commit_author",
models.CharField(
blank=True, help_text="Author of the commit.", max_length=100, null=True
),
),
(
"commit_date",
models.DateTimeField(
blank=True,
help_text="Timestamp indicating when this commit was created.",
null=True,
),
),
(
"commit_message",
models.TextField(
blank=True, help_text="Commit message or description.", null=True
),
),
],
options={
"unique_together": {("commit_hash", "vcs_url")},
},
),
migrations.AddField(
model_name="impactedpackage",
name="affecting_commits",
field=models.ManyToManyField(
help_text="Commits introducing this impact.",
related_name="affecting_commits_in_impacts",
to="vulnerabilities.codecommit",
),
),
migrations.AddField(
model_name="impactedpackage",
name="fixed_by_commits",
field=models.ManyToManyField(
help_text="Commits fixing this impact.",
related_name="fixing_commits_in_impacts",
to="vulnerabilities.codecommit",
),
),
]
41 changes: 41 additions & 0 deletions vulnerabilities/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2957,6 +2957,18 @@ class ImpactedPackage(models.Model):
help_text="Packages vulnerable to this impact.",
)

affecting_commits = models.ManyToManyField(
"CodeCommit",
related_name="affecting_commits_in_impacts",
help_text="Commits introducing this impact.",
)

fixed_by_commits = models.ManyToManyField(
"CodeCommit",
related_name="fixing_commits_in_impacts",
help_text="Commits fixing this impact.",
)

created_at = models.DateTimeField(
auto_now_add=True,
db_index=True,
Expand Down Expand Up @@ -3373,3 +3385,32 @@ class AdvisoryExploit(models.Model):
@property
def get_known_ransomware_campaign_use_type(self):
return "Known" if self.known_ransomware_campaign_use else "Unknown"


class CodeCommit(models.Model):
"""
A CodeCommit Represents a single VCS commit (e.g., Git) related to a ImpactedPackage.
"""

commit_hash = models.CharField(max_length=64, help_text="Unique commit identifier (e.g., SHA).")
vcs_url = models.URLField(
max_length=1024, help_text="URL of the repository containing the commit."
)

commit_rank = models.IntegerField(
default=0,
help_text="Rank of the commit to support ordering by commit. Rank "
"zero means the rank has not been defined yet",
)
commit_author = models.CharField(
max_length=100, null=True, blank=True, help_text="Author of the commit."
)
commit_date = models.DateTimeField(
null=True, blank=True, help_text="Timestamp indicating when this commit was created."
)
commit_message = models.TextField(
null=True, blank=True, help_text="Commit message or description."
)

class Meta:
unique_together = ("commit_hash", "vcs_url")
58 changes: 58 additions & 0 deletions vulnerabilities/tests/test_commit_code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from datetime import datetime

import pytest

from vulnerabilities.models import AdvisoryV2
from vulnerabilities.models import CodeCommit
from vulnerabilities.models import ImpactedPackage


@pytest.mark.django_db
class TestCodeCommit:
def setup_method(self):
date = datetime.now()
adv = AdvisoryV2.objects.create(
unique_content_id="test_id",
url="https://example.com",
summary="summary",
date_imported=date,
date_collected=date,
advisory_id="test_id",
avid="test_pipeline/test_id",
datasource_id="test_pipeline",
)

self.impacted = ImpactedPackage.objects.create(
advisory=adv,
base_purl="pkg:pypi/redis",
)

self.code_commit1 = CodeCommit.objects.create(
commit_hash="8c001a11dbcb3eb6d851e18f4cefa080af5fb398",
vcs_url="https://github.com/aboutcode-org/test1/",
commit_author="tester1",
commit_message="test message1",
commit_date=datetime.now(),
)

self.code_commit2 = CodeCommit.objects.create(
commit_hash="8c001a1",
vcs_url="https://github.com/aboutcode-org/test1/",
)

self.impacted.fixed_by_commits.add(self.code_commit1)
self.impacted.affecting_commits.add(self.code_commit2)

def test_commits_are_created(self):
commits = CodeCommit.objects.all()
assert commits.count() == 2

def test_commit_fields(self):
commit = CodeCommit.objects.get(commit_hash="8c001a11dbcb3eb6d851e18f4cefa080af5fb398")
assert commit.commit_author == "tester1"
assert "test message1" == commit.commit_message
assert commit.commit_date is not None

def test_impacted_packages_creation(self):
assert ImpactedPackage.objects.count() == 1
assert self.code_commit1 == self.impacted.fixed_by_commits.first()