Skip to content

Commit

Permalink
Add weighted_risk_score field and logic on ProductRelationship #102
Browse files Browse the repository at this point in the history
Signed-off-by: tdruez <tdruez@nexb.com>
  • Loading branch information
tdruez committed Dec 20, 2024
1 parent 9ed3f8e commit ad8a4fe
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 27 deletions.
9 changes: 3 additions & 6 deletions product_portfolio/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ class Meta:

class BaseProductRelationFilterSet(DataspacedFilterSet):
field_name_prefix = None
dropdown_fields = ["is_modified", "weighted_risk_score"]
is_deployed = BooleanChoiceFilter(
empty_label="All (Inventory)",
choices=(
Expand Down Expand Up @@ -161,7 +162,7 @@ class BaseProductRelationFilterSet(DataspacedFilterSet):
label=_("Severity"),
score_ranges=RISK_SCORE_RANGES,
)
risk_score = ScoreRangeFilter(
weighted_risk_score = ScoreRangeFilter(
label=_("Risk score"),
score_ranges=RISK_SCORE_RANGES,
)
Expand Down Expand Up @@ -192,12 +193,8 @@ def __init__(self, *args, **kwargs):
self.filters["purpose"].extra["to_field_name"] = "label"
self.filters["purpose"].extra["widget"] = DropDownWidget(anchor=self.anchor)

self.filters["is_modified"].extra["widget"] = DropDownWidget(
anchor=self.anchor, right_align=True
)

field_name_prefix = self.field_name_prefix
for field_name in ["exploitability", "weighted_severity", "risk_score"]:
for field_name in ["exploitability", "weighted_severity"]:
field = self.filters[field_name]
field.extra["widget"] = DropDownWidget(anchor=self.anchor)
field.field_name = f"{field_name_prefix}__{field_name}"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Generated by Django 5.0.9 on 2024-12-20 10:20

import django.core.validators
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('product_portfolio', '0009_product_vulnerabilities_risk_threshold'),
]

operations = [
migrations.AddField(
model_name='productcomponent',
name='weighted_risk_score',
field=models.DecimalField(blank=True, decimal_places=1, help_text='Risk score from 0.0 to 10.0, with higher values indicating greater vulnerability risk. This score is the maximum of the weighted severity multiplied by exploitability, capped at 10, which is then multiplied by the associated exposure risk factor assigned to the product package purpose (when available).', max_digits=3, null=True),
),
migrations.AddField(
model_name='productitempurpose',
name='exposure_factor',
field=models.DecimalField(blank=True, decimal_places=1, help_text='A number between 0.0 and 1.0 that identifies the vulnerability exposure risk of a package as it is actually used in the context of a product, with 1.0 being the highest exposure risk and 0.0 being no exposure risk at all.', max_digits=2, null=True, validators=[django.core.validators.MaxValueValidator(1.0), django.core.validators.MinValueValidator(0.0)]),
),
migrations.AddField(
model_name='productpackage',
name='weighted_risk_score',
field=models.DecimalField(blank=True, decimal_places=1, help_text='Risk score from 0.0 to 10.0, with higher values indicating greater vulnerability risk. This score is the maximum of the weighted severity multiplied by exploitability, capped at 10, which is then multiplied by the associated exposure risk factor assigned to the product package purpose (when available).', max_digits=3, null=True),
),
]

This file was deleted.

30 changes: 30 additions & 0 deletions product_portfolio/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -702,13 +702,29 @@ class ProductRelationshipMixin(
),
)

weighted_risk_score = models.DecimalField(
null=True,
blank=True,
max_digits=3,
decimal_places=1,
help_text=_(
"Risk score from 0.0 to 10.0, with higher values indicating greater "
"vulnerability risk. This score is the maximum of the weighted severity "
"multiplied by exploitability, capped at 10, which is then multiplied by "
"the associated exposure risk factor assigned to the product package "
"purpose (when available)."
),
)

class Meta:
abstract = True

def save(self, *args, **kwargs):
is_addition = not self.pk
if is_addition:
self.set_review_status_from_policy()

self.set_weighted_risk_score()
super().save(*args, **kwargs)

def set_review_status_from_policy(self):
Expand All @@ -720,6 +736,20 @@ def set_review_status_from_policy(self):
if status_from_policy := self.get_status_from_item_policy():
self.review_status = status_from_policy

def compute_weighted_risk_score(self):
exposure_factor = 1.0
if self.purpose and self.purpose.exposure_factor is not None:
exposure_factor = self.purpose.exposure_factor

if self.package.risk_score is not None:
weighted_risk_score = self.package.risk_score * exposure_factor
return weighted_risk_score

def set_weighted_risk_score(self):
weighted_risk_score = self.compute_weighted_risk_score()
if weighted_risk_score != self.weighted_risk_score:
self.weighted_risk_score = weighted_risk_score

def get_status_from_item_policy(self):
"""
Return the `associated_product_relation_status` from the related item
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
<span class="help_text" data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-title="{{ help_texts.risk_score }}">
{% trans 'Risk' %}
</span>
{{ filter_productcomponent.form.risk_score }}
{{ filter_productcomponent.form.weighted_risk_score }}
</th>
{% endif %}
</tr>
Expand Down Expand Up @@ -134,7 +134,7 @@
{% if product.dataspace.enable_vulnerablecodedb_access %}
<td class="fs-110pct">
{% if relation.related_component_or_package.vulnerability_count %}
{% include 'vulnerabilities/includes/risk_score_badge.html' with risk_score=relation.related_component_or_package.risk_score only %}
{% include 'vulnerabilities/includes/risk_score_badge.html' with risk_score=relation.weighted_risk_score only %}
{% endif %}
</td>
{% endif %}
Expand Down

0 comments on commit ad8a4fe

Please sign in to comment.