Skip to content

Commit

Permalink
Move the vulnerabilities related code to its own module #95 (#177)
Browse files Browse the repository at this point in the history
Signed-off-by: tdruez <tdruez@nexb.com>
  • Loading branch information
tdruez committed Sep 5, 2024
1 parent 29c7d32 commit f32fcef
Show file tree
Hide file tree
Showing 55 changed files with 1,315 additions and 852 deletions.
82 changes: 0 additions & 82 deletions component_catalog/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

from django import forms
from django.contrib.admin.options import IncorrectLookupParameters
from django.db.models import F
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _

Expand All @@ -17,14 +16,12 @@
from component_catalog.models import Component
from component_catalog.models import ComponentKeyword
from component_catalog.models import Package
from component_catalog.models import Vulnerability
from component_catalog.programming_languages import PROGRAMMING_LANGUAGES
from dje.filters import DataspacedFilterSet
from dje.filters import DefaultOrderingFilter
from dje.filters import HasRelationFilter
from dje.filters import MatchOrderedSearchFilter
from dje.filters import RelatedLookupListFilter
from dje.filters import SearchFilter
from dje.widgets import BootstrapSelectMultipleWidget
from dje.widgets import DropDownRightWidget
from dje.widgets import SortDropDownWidget
Expand Down Expand Up @@ -283,82 +280,3 @@ def show_created_date(self):
@cached_property
def show_last_modified_date(self):
return not self.sort_value or self.has_sort_by("last_modified_date")


class NullsLastOrderingFilter(django_filters.OrderingFilter):
"""
A custom ordering filter that ensures null values are sorted last.
When sorting by fields with potential null values, this filter modifies the
ordering to use Django's `nulls_last` clause for better handling of null values,
whether in ascending or descending order.
"""

def filter(self, qs, value):
if not value:
return qs

ordering = []
for field in value:
if field.startswith("-"):
field_name = field[1:]
ordering.append(F(field_name).desc(nulls_last=True))
else:
ordering.append(F(field).asc(nulls_last=True))

return qs.order_by(*ordering)


vulnerability_score_ranges = {
"low": (0.1, 3),
"medium": (4.0, 6.9),
"high": (7.0, 8.9),
"critical": (9.0, 10.0),
}

SCORE_CHOICES = [
(key, f"{key.capitalize()} ({value[0]} - {value[1]})")
for key, value in vulnerability_score_ranges.items()
]


class VulnerabilityFilterSet(DataspacedFilterSet):
q = SearchFilter(
label=_("Search"),
search_fields=["vulnerability_id", "aliases"],
)
sort = NullsLastOrderingFilter(
label=_("Sort"),
fields=[
"max_score",
"min_score",
"affected_products_count",
"affected_packages_count",
"fixed_packages_count",
"created_date",
"last_modified_date",
],
widget=SortDropDownWidget,
)
max_score = django_filters.ChoiceFilter(
choices=SCORE_CHOICES,
method="filter_by_score_range",
label="Score Range",
help_text="Select a score range to filter.",
)

class Meta:
model = Vulnerability
fields = [
"q",
]

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.filters["max_score"].extra["widget"] = DropDownRightWidget(anchor=self.anchor)

def filter_by_score_range(self, queryset, name, value):
if value in vulnerability_score_ranges:
low, high = vulnerability_score_ranges[value]
return queryset.filter(max_score__gte=low, max_score__lte=high)
return queryset
2 changes: 1 addition & 1 deletion component_catalog/importers.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
from component_catalog.models import Package
from component_catalog.models import Subcomponent
from component_catalog.programming_languages import PROGRAMMING_LANGUAGES
from component_catalog.vulnerabilities import fetch_for_queryset
from dje.fields import SmartFileField
from dje.forms import JSONListField
from dje.importers import BaseImporter
Expand All @@ -42,6 +41,7 @@
from policy.models import UsagePolicy
from product_portfolio.models import ProductComponent
from product_portfolio.models import ProductPackage
from vulnerabilities.fetch import fetch_for_queryset

keywords_help = (
get_help_text(Component, "keywords")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 5.0.6 on 2024-09-04 08:17

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('component_catalog', '0007_vulnerability_fixed_packages_count_and_more'),
]

operations = [
migrations.RemoveField(
model_name='package',
name='affected_by_vulnerabilities',
),
migrations.RemoveField(
model_name='component',
name='affected_by_vulnerabilities',
),
migrations.DeleteModel(
name='Vulnerability',
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Generated by Django 5.0.6 on 2024-09-04 09:22

import django.db.models.deletion
import uuid
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('component_catalog', '0008_remove_package_affected_by_vulnerabilities_and_more'),
('dje', '0004_dataspace_vulnerabilities_updated_at'),
('vulnerabilities', '0001_initial'),
]

operations = [
migrations.CreateModel(
name='ComponentAffectedByVulnerability',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, verbose_name='UUID')),
('component', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='component_catalog.component')),
('dataspace', models.ForeignKey(editable=False, help_text='A Dataspace is an independent, exclusive set of DejaCode data, which can be either nexB master reference data or installation-specific data.', on_delete=django.db.models.deletion.PROTECT, to='dje.dataspace')),
('vulnerability', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='vulnerabilities.vulnerability')),
],
options={
'unique_together': {('component', 'vulnerability'), ('dataspace', 'uuid')},
},
),
migrations.AddField(
model_name='component',
name='affected_by_vulnerabilities',
field=models.ManyToManyField(help_text='Vulnerabilities affecting this object.', related_name='affected_%(class)ss', through='component_catalog.ComponentAffectedByVulnerability', to='vulnerabilities.vulnerability'),
),
migrations.CreateModel(
name='PackageAffectedByVulnerability',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('uuid', models.UUIDField(default=uuid.uuid4, editable=False, verbose_name='UUID')),
('dataspace', models.ForeignKey(editable=False, help_text='A Dataspace is an independent, exclusive set of DejaCode data, which can be either nexB master reference data or installation-specific data.', on_delete=django.db.models.deletion.PROTECT, to='dje.dataspace')),
('package', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='component_catalog.package')),
('vulnerability', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='vulnerabilities.vulnerability')),
],
options={
'unique_together': {('dataspace', 'uuid'), ('package', 'vulnerability')},
},
),
migrations.AddField(
model_name='package',
name='affected_by_vulnerabilities',
field=models.ManyToManyField(help_text='Vulnerabilities affecting this object.', related_name='affected_%(class)ss', through='component_catalog.PackageAffectedByVulnerability', to='vulnerabilities.vulnerability'),
),
]
Loading

0 comments on commit f32fcef

Please sign in to comment.