diff --git a/vulnerabilities/api.py b/vulnerabilities/api.py index f15a884eb..309c2d838 100644 --- a/vulnerabilities/api.py +++ b/vulnerabilities/api.py @@ -30,6 +30,7 @@ from rest_framework.decorators import action from rest_framework.response import Response +from vulnerabilities.models import Alias from vulnerabilities.models import Package from vulnerabilities.models import Vulnerability from vulnerabilities.models import VulnerabilityReference @@ -47,7 +48,7 @@ class VulnerabilityReferenceSerializer(serializers.ModelSerializer): class Meta: model = VulnerabilityReference - fields = ["reference_id", "url", "scores"] + fields = ["url", "reference_id", "scores"] class MinimalPackageSerializer(serializers.HyperlinkedModelSerializer): @@ -74,18 +75,36 @@ class Meta: fields = ["url", "vulnerability_id", "references", "summary"] +class AliasSerializer(serializers.HyperlinkedModelSerializer): + """ + Used for nesting inside package focused APIs. + """ + + class Meta: + model = Alias + fields = ["alias"] + + class VulnerabilitySerializer(serializers.HyperlinkedModelSerializer): resolved_packages = MinimalPackageSerializer(many=True, source="resolved_to", read_only=True) - unresolved_packages = MinimalPackageSerializer( - many=True, source="vulnerable_to", read_only=True - ) + affected_packages = MinimalPackageSerializer(many=True, source="vulnerable_to", read_only=True) references = VulnerabilityReferenceSerializer(many=True, source="vulnerabilityreference_set") + alias = AliasSerializer(many=True) class Meta: model = Vulnerability - fields = "__all__" + fields = [ + "url", + "vulcoid", + "summary", + "alias", + "vulnerability_id", + "resolved_packages", + "affected_packages", + "references", + ] class PackageSerializer(serializers.HyperlinkedModelSerializer): diff --git a/vulnerabilities/migrations/0010_vulnerability_packages_alter_package_vulnerabilities_and_more.py b/vulnerabilities/migrations/0010_vulnerability_packages_alter_package_vulnerabilities_and_more.py new file mode 100644 index 000000000..d86aa46f2 --- /dev/null +++ b/vulnerabilities/migrations/0010_vulnerability_packages_alter_package_vulnerabilities_and_more.py @@ -0,0 +1,29 @@ +# Generated by Django 4.0.3 on 2022-04-15 19:16 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('vulnerabilities', '0009_alter_advisory_summary_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='vulnerability', + name='packages', + field=models.ManyToManyField(through='vulnerabilities.PackageRelatedVulnerability', to='vulnerabilities.package'), + ), + migrations.AlterField( + model_name='package', + name='vulnerabilities', + field=models.ManyToManyField(through='vulnerabilities.PackageRelatedVulnerability', to='vulnerabilities.vulnerability'), + ), + migrations.AlterField( + model_name='packagerelatedvulnerability', + name='package', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='vulnerabilities.package'), + ), + ] diff --git a/vulnerabilities/models.py b/vulnerabilities/models.py index dee7773aa..d959f4b84 100644 --- a/vulnerabilities/models.py +++ b/vulnerabilities/models.py @@ -29,6 +29,7 @@ from django.core.validators import MaxValueValidator from django.core.validators import MinValueValidator from django.db import models +from django.utils.http import int_to_base36 from packageurl import PackageURL from packageurl.contrib.django.models import PackageURLMixin @@ -60,16 +61,21 @@ class Vulnerability(models.Model): blank=True, ) + packages = models.ManyToManyField( + to="Package", + through="PackageRelatedVulnerability", + ) + @property def vulcoid(self): - return f"VULCOID-{self.vulnerability_id}" + return f"VULCOID-{int_to_base36(self.id).upper()}" @property def vulnerable_to(self): """ Return packages that are vulnerable to this vulnerability. """ - return self.packages.filter(vulnerabilities__packagerelatedvulnerability__fix=False) + return self.packages.filter(packagerelatedvulnerability__fix=False) @property def resolved_to(self): @@ -77,7 +83,15 @@ def resolved_to(self): Returns packages that first received patch against this vulnerability in their particular version history. """ - return self.packages.filter(vulnerabilities__packagerelatedvulnerability__fix=True) + return self.packages.filter(packagerelatedvulnerability__fix=True) + + @property + def alias(self): + """ + Returns packages that first received patch against this vulnerability + in their particular version history. + """ + return self.aliases.all() def __str__(self): return self.vulcoid @@ -127,10 +141,7 @@ class Package(PackageURLMixin): """ vulnerabilities = models.ManyToManyField( - to="Vulnerability", - through="PackageRelatedVulnerability", - through_fields=("package", "vulnerability"), - related_name="packages", + to="Vulnerability", through="PackageRelatedVulnerability" ) # Remove the `qualifers` and `set_package_url` overrides after @@ -195,8 +206,14 @@ def __str__(self): class PackageRelatedVulnerability(models.Model): # TODO: Fix related_name - package = models.ForeignKey(Package, on_delete=models.CASCADE, related_name="package") - vulnerability = models.ForeignKey(Vulnerability, on_delete=models.CASCADE) + package = models.ForeignKey( + Package, + on_delete=models.CASCADE, + ) + vulnerability = models.ForeignKey( + Vulnerability, + on_delete=models.CASCADE, + ) created_by = models.CharField( max_length=100, blank=True, @@ -223,6 +240,10 @@ class Meta: verbose_name_plural = "PackageRelatedVulnerabilities" indexes = [models.Index(fields=["fix"])] + @property + def purl(self): + return self.package.package_url + def update_or_create(self): """ Update if supplied record has more confidence than existing record diff --git a/vulnerabilities/templates/package_update.html b/vulnerabilities/templates/package_update.html index 881b3ef84..b45afec74 100644 --- a/vulnerabilities/templates/package_update.html +++ b/vulnerabilities/templates/package_update.html @@ -30,7 +30,7 @@
Vulnerable To
+Affected By