Skip to content

Commit

Permalink
Create a pipeline for package risk
Browse files Browse the repository at this point in the history
Signed-off-by: ziadhany <[email protected]>
  • Loading branch information
ziadhany committed Oct 22, 2024
1 parent ce4bfba commit 8d9f6d8
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 43 deletions.
1 change: 1 addition & 0 deletions vulnerabilities/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ class Meta:
"latest_non_vulnerable_version",
"affected_by_vulnerabilities",
"fixing_vulnerabilities",
"risk",
]


Expand Down
44 changes: 23 additions & 21 deletions vulnerabilities/improvers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,31 @@
from vulnerabilities.pipelines import enhance_with_kev
from vulnerabilities.pipelines import enhance_with_metasploit
from vulnerabilities.pipelines import flag_ghost_packages
from vulnerabilities.pipelines import risk_package

IMPROVERS_REGISTRY = [
valid_versions.GitHubBasicImprover,
valid_versions.GitLabBasicImprover,
valid_versions.NginxBasicImprover,
valid_versions.ApacheHTTPDImprover,
valid_versions.DebianBasicImprover,
valid_versions.NpmImprover,
valid_versions.ElixirImprover,
valid_versions.ApacheTomcatImprover,
valid_versions.ApacheKafkaImprover,
valid_versions.IstioImprover,
valid_versions.DebianOvalImprover,
valid_versions.UbuntuOvalImprover,
valid_versions.OSSFuzzImprover,
valid_versions.RubyImprover,
valid_versions.GithubOSVImprover,
vulnerability_status.VulnerabilityStatusImprover,
valid_versions.CurlImprover,
flag_ghost_packages.FlagGhostPackagePipeline,
enhance_with_kev.VulnerabilityKevPipeline,
enhance_with_metasploit.MetasploitImproverPipeline,
enhance_with_exploitdb.ExploitDBImproverPipeline,
# valid_versions.GitHubBasicImprover,
# valid_versions.GitLabBasicImprover,
# valid_versions.NginxBasicImprover,
# valid_versions.ApacheHTTPDImprover,
# valid_versions.DebianBasicImprover,
# valid_versions.NpmImprover,
# valid_versions.ElixirImprover,
# valid_versions.ApacheTomcatImprover,
# valid_versions.ApacheKafkaImprover,
# valid_versions.IstioImprover,
# valid_versions.DebianOvalImprover,
# valid_versions.UbuntuOvalImprover,
# valid_versions.OSSFuzzImprover,
# valid_versions.RubyImprover,
# valid_versions.GithubOSVImprover,
# vulnerability_status.VulnerabilityStatusImprover,
# valid_versions.CurlImprover,
# flag_ghost_packages.FlagGhostPackagePipeline,
# enhance_with_kev.VulnerabilityKevPipeline,
# enhance_with_metasploit.MetasploitImproverPipeline,
# enhance_with_exploitdb.ExploitDBImproverPipeline,
risk_package.RiskPackagePipeline,
]

IMPROVERS_REGISTRY = {
Expand Down
23 changes: 23 additions & 0 deletions vulnerabilities/migrations/0074_package_risk.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 4.2.16 on 2024-10-22 06:49

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("vulnerabilities", "0073_delete_packagerelatedvulnerability"),
]

operations = [
migrations.AddField(
model_name="package",
name="risk",
field=models.DecimalField(
decimal_places=2,
help_text="Enter a risk score between 0.00 and 10.00, where higher values indicate greater vulnerability risk for the package.",
max_digits=4,
null=True,
),
),
]
8 changes: 8 additions & 0 deletions vulnerabilities/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,14 @@ class Package(PackageURLMixin):
help_text="True if the package does not exist in the upstream package manager or its repository.",
)

risk = models.DecimalField(
null=True,
max_digits=4,
decimal_places=2,
help_text="Enter a risk score between 0.00 and 10.00, where higher values "
"indicate greater vulnerability risk for the package.",
)

objects = PackageQuerySet.as_manager()

def save(self, *args, **kwargs):
Expand Down
30 changes: 30 additions & 0 deletions vulnerabilities/pipelines/risk_package.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from vulnerabilities.models import Package
from vulnerabilities.pipelines import VulnerableCodePipeline
from vulnerabilities.risk import calculate_pkg_risk


class RiskPackagePipeline(VulnerableCodePipeline):
"""
Risk Assessment Pipeline for Package Vulnerabilities: Iterate through the packages and evaluate their associated risk.
"""

pipeline_id = "risk_package"
license_expression = None

@classmethod
def steps(cls):
return (cls.add_risk_package,)

def add_risk_package(self):
self.log(f"Add risk package pipeline ")

updatables = []
for pkg in Package.objects.filter(affected_by_vulnerabilities__isnull=False):
risk = calculate_pkg_risk(pkg)
pkg.risk = risk
updatables.append(pkg)

# Bulk update the 'risk' field for all packages
Package.objects.bulk_update(objs=updatables, fields=["risk"], batch_size=1000)

self.log(f"Successfully added risk package pipeline ")
33 changes: 17 additions & 16 deletions vulnerabilities/risk.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import os
import re

from vulnerabilities.models import AffectedByPackageRelatedVulnerability
from vulnerabilities.models import Exploit
from vulnerabilities.models import Package
from vulnerabilities.models import PackageRelatedVulnerability
from vulnerabilities.models import Vulnerability
from vulnerabilities.models import VulnerabilityReference
from vulnerabilities.severity_systems import EPSS
from vulnerabilities.utils import load_json

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
WEIGHT_CONFIG_PATH = os.path.join(BASE_DIR, "../weight_config.json")
DEFAULT_WEIGHT = 1


def get_weighted_severity(severities):
Expand All @@ -18,8 +19,8 @@ def get_weighted_severity(severities):
by its associated Weight/10.
Example of Weighted Severity: max(7*(10/10), 8*(3/10), 6*(8/10)) = 7
"""
weight_config_path = os.path.join(BASE_DIR, "..", "weight_config.json")
weight_config = load_json(weight_config_path)

weight_config = load_json(WEIGHT_CONFIG_PATH)

score_map = {
"low": 3,
Expand All @@ -33,11 +34,12 @@ def get_weighted_severity(severities):

score_list = []
for severity in severities:
weights = [
value
for regex_key, value in weight_config.items()
if re.match(regex_key, severity.reference.url)
]
weights = []
for key, value in weight_config.items():
if severity.reference.url.startswith(key):
weights.append(value)
continue
weights.append(DEFAULT_WEIGHT)

if not weights:
return 0
Expand Down Expand Up @@ -113,14 +115,13 @@ def calculate_pkg_risk(package: Package):
"""

result = []
for pkg_related_vul in PackageRelatedVulnerability.objects.filter(
package=package, fix=False
for pkg_related_vul in AffectedByPackageRelatedVulnerability.objects.filter(
package=package
).prefetch_related("vulnerability"):
if pkg_related_vul:
risk = calculate_vulnerability_risk(pkg_related_vul.vulnerability)
if not risk:
continue
result.append(risk)
risk = calculate_vulnerability_risk(pkg_related_vul.vulnerability)
if not risk:
continue
result.append(risk)

if not result:
return
Expand Down
4 changes: 2 additions & 2 deletions vulnerabilities/templates/package_details.html
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@
Risk
</td>
<td class="two-col-right">
{% if risk %}
<a target="_self">{{ risk }}</a>
{% if package.risk %}
<a target="_self">{{ package.risk }}</a>
{% endif %}
</td>
</tr>
Expand Down
24 changes: 24 additions & 0 deletions vulnerabilities/tests/pipelines/test_risk_pipeline.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import pytest

from vulnerabilities.models import AffectedByPackageRelatedVulnerability
from vulnerabilities.models import Package
from vulnerabilities.pipelines.risk_package import RiskPackagePipeline
from vulnerabilities.tests.test_risk import vulnerability


@pytest.mark.django_db
def test_simple_risk_pipeline(vulnerability):
pkg = Package.objects.create(type="pypi", name="foo", version="2.3.0")
assert Package.objects.count() == 1

improver = RiskPackagePipeline()
improver.execute()

assert pkg.risk is None

AffectedByPackageRelatedVulnerability.objects.create(package=pkg, vulnerability=vulnerability)
improver = RiskPackagePipeline()
improver.execute()

pkg = Package.objects.get(type="pypi", name="foo", version="2.3.0")
assert str(pkg.risk) == str(3.11)
1 change: 1 addition & 0 deletions vulnerabilities/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,7 @@ def test_api_with_lesser_and_greater_fixed_by_packages(self):
}
],
"resource_url": "http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/[email protected]",
"risk": None,
}

assert response == expected
Expand Down
1 change: 0 additions & 1 deletion vulnerabilities/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ def get_context_data(self, **kwargs):
context["fixing_vulnerabilities"] = package.fixing.order_by("vulnerability_id")
context["package_search_form"] = PackageSearchForm(self.request.GET)
context["fixed_package_details"] = package.fixed_package_details
context["risk"] = calculate_pkg_risk(package)
context["history"] = list(package.history)
return context

Expand Down
5 changes: 2 additions & 3 deletions weight_config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"https://nvd\\.nist\\.gov/.*": 9,
"https:\\/\\/security-tracker\\.debian\\.org\\/.*": 9,
"^(?:http|ftp)s?://": 1
"https://nvd.nist.gov/": 9,
"https://security-tracker.debian.org/": 9
}

0 comments on commit 8d9f6d8

Please sign in to comment.