Skip to content

Commit 8d9f6d8

Browse files
committed
Create a pipeline for package risk
Signed-off-by: ziadhany <ziadhany2016@gmail.com>
1 parent ce4bfba commit 8d9f6d8

11 files changed

Lines changed: 131 additions & 43 deletions

File tree

vulnerabilities/api.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ class Meta:
359359
"latest_non_vulnerable_version",
360360
"affected_by_vulnerabilities",
361361
"fixing_vulnerabilities",
362+
"risk",
362363
]
363364

364365

vulnerabilities/improvers/__init__.py

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,29 +14,31 @@
1414
from vulnerabilities.pipelines import enhance_with_kev
1515
from vulnerabilities.pipelines import enhance_with_metasploit
1616
from vulnerabilities.pipelines import flag_ghost_packages
17+
from vulnerabilities.pipelines import risk_package
1718

1819
IMPROVERS_REGISTRY = [
19-
valid_versions.GitHubBasicImprover,
20-
valid_versions.GitLabBasicImprover,
21-
valid_versions.NginxBasicImprover,
22-
valid_versions.ApacheHTTPDImprover,
23-
valid_versions.DebianBasicImprover,
24-
valid_versions.NpmImprover,
25-
valid_versions.ElixirImprover,
26-
valid_versions.ApacheTomcatImprover,
27-
valid_versions.ApacheKafkaImprover,
28-
valid_versions.IstioImprover,
29-
valid_versions.DebianOvalImprover,
30-
valid_versions.UbuntuOvalImprover,
31-
valid_versions.OSSFuzzImprover,
32-
valid_versions.RubyImprover,
33-
valid_versions.GithubOSVImprover,
34-
vulnerability_status.VulnerabilityStatusImprover,
35-
valid_versions.CurlImprover,
36-
flag_ghost_packages.FlagGhostPackagePipeline,
37-
enhance_with_kev.VulnerabilityKevPipeline,
38-
enhance_with_metasploit.MetasploitImproverPipeline,
39-
enhance_with_exploitdb.ExploitDBImproverPipeline,
20+
# valid_versions.GitHubBasicImprover,
21+
# valid_versions.GitLabBasicImprover,
22+
# valid_versions.NginxBasicImprover,
23+
# valid_versions.ApacheHTTPDImprover,
24+
# valid_versions.DebianBasicImprover,
25+
# valid_versions.NpmImprover,
26+
# valid_versions.ElixirImprover,
27+
# valid_versions.ApacheTomcatImprover,
28+
# valid_versions.ApacheKafkaImprover,
29+
# valid_versions.IstioImprover,
30+
# valid_versions.DebianOvalImprover,
31+
# valid_versions.UbuntuOvalImprover,
32+
# valid_versions.OSSFuzzImprover,
33+
# valid_versions.RubyImprover,
34+
# valid_versions.GithubOSVImprover,
35+
# vulnerability_status.VulnerabilityStatusImprover,
36+
# valid_versions.CurlImprover,
37+
# flag_ghost_packages.FlagGhostPackagePipeline,
38+
# enhance_with_kev.VulnerabilityKevPipeline,
39+
# enhance_with_metasploit.MetasploitImproverPipeline,
40+
# enhance_with_exploitdb.ExploitDBImproverPipeline,
41+
risk_package.RiskPackagePipeline,
4042
]
4143

4244
IMPROVERS_REGISTRY = {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Generated by Django 4.2.16 on 2024-10-22 06:49
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("vulnerabilities", "0073_delete_packagerelatedvulnerability"),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name="package",
15+
name="risk",
16+
field=models.DecimalField(
17+
decimal_places=2,
18+
help_text="Enter a risk score between 0.00 and 10.00, where higher values indicate greater vulnerability risk for the package.",
19+
max_digits=4,
20+
null=True,
21+
),
22+
),
23+
]

vulnerabilities/models.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,14 @@ class Package(PackageURLMixin):
636636
help_text="True if the package does not exist in the upstream package manager or its repository.",
637637
)
638638

639+
risk = models.DecimalField(
640+
null=True,
641+
max_digits=4,
642+
decimal_places=2,
643+
help_text="Enter a risk score between 0.00 and 10.00, where higher values "
644+
"indicate greater vulnerability risk for the package.",
645+
)
646+
639647
objects = PackageQuerySet.as_manager()
640648

641649
def save(self, *args, **kwargs):
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from vulnerabilities.models import Package
2+
from vulnerabilities.pipelines import VulnerableCodePipeline
3+
from vulnerabilities.risk import calculate_pkg_risk
4+
5+
6+
class RiskPackagePipeline(VulnerableCodePipeline):
7+
"""
8+
Risk Assessment Pipeline for Package Vulnerabilities: Iterate through the packages and evaluate their associated risk.
9+
"""
10+
11+
pipeline_id = "risk_package"
12+
license_expression = None
13+
14+
@classmethod
15+
def steps(cls):
16+
return (cls.add_risk_package,)
17+
18+
def add_risk_package(self):
19+
self.log(f"Add risk package pipeline ")
20+
21+
updatables = []
22+
for pkg in Package.objects.filter(affected_by_vulnerabilities__isnull=False):
23+
risk = calculate_pkg_risk(pkg)
24+
pkg.risk = risk
25+
updatables.append(pkg)
26+
27+
# Bulk update the 'risk' field for all packages
28+
Package.objects.bulk_update(objs=updatables, fields=["risk"], batch_size=1000)
29+
30+
self.log(f"Successfully added risk package pipeline ")

vulnerabilities/risk.py

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
import os
2-
import re
32

3+
from vulnerabilities.models import AffectedByPackageRelatedVulnerability
44
from vulnerabilities.models import Exploit
55
from vulnerabilities.models import Package
6-
from vulnerabilities.models import PackageRelatedVulnerability
76
from vulnerabilities.models import Vulnerability
87
from vulnerabilities.models import VulnerabilityReference
98
from vulnerabilities.severity_systems import EPSS
109
from vulnerabilities.utils import load_json
1110

1211
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
12+
WEIGHT_CONFIG_PATH = os.path.join(BASE_DIR, "../weight_config.json")
13+
DEFAULT_WEIGHT = 1
1314

1415

1516
def get_weighted_severity(severities):
@@ -18,8 +19,8 @@ def get_weighted_severity(severities):
1819
by its associated Weight/10.
1920
Example of Weighted Severity: max(7*(10/10), 8*(3/10), 6*(8/10)) = 7
2021
"""
21-
weight_config_path = os.path.join(BASE_DIR, "..", "weight_config.json")
22-
weight_config = load_json(weight_config_path)
22+
23+
weight_config = load_json(WEIGHT_CONFIG_PATH)
2324

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

3435
score_list = []
3536
for severity in severities:
36-
weights = [
37-
value
38-
for regex_key, value in weight_config.items()
39-
if re.match(regex_key, severity.reference.url)
40-
]
37+
weights = []
38+
for key, value in weight_config.items():
39+
if severity.reference.url.startswith(key):
40+
weights.append(value)
41+
continue
42+
weights.append(DEFAULT_WEIGHT)
4143

4244
if not weights:
4345
return 0
@@ -113,14 +115,13 @@ def calculate_pkg_risk(package: Package):
113115
"""
114116

115117
result = []
116-
for pkg_related_vul in PackageRelatedVulnerability.objects.filter(
117-
package=package, fix=False
118+
for pkg_related_vul in AffectedByPackageRelatedVulnerability.objects.filter(
119+
package=package
118120
).prefetch_related("vulnerability"):
119-
if pkg_related_vul:
120-
risk = calculate_vulnerability_risk(pkg_related_vul.vulnerability)
121-
if not risk:
122-
continue
123-
result.append(risk)
121+
risk = calculate_vulnerability_risk(pkg_related_vul.vulnerability)
122+
if not risk:
123+
continue
124+
result.append(risk)
124125

125126
if not result:
126127
return

vulnerabilities/templates/package_details.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@
117117
Risk
118118
</td>
119119
<td class="two-col-right">
120-
{% if risk %}
121-
<a target="_self">{{ risk }}</a>
120+
{% if package.risk %}
121+
<a target="_self">{{ package.risk }}</a>
122122
{% endif %}
123123
</td>
124124
</tr>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import pytest
2+
3+
from vulnerabilities.models import AffectedByPackageRelatedVulnerability
4+
from vulnerabilities.models import Package
5+
from vulnerabilities.pipelines.risk_package import RiskPackagePipeline
6+
from vulnerabilities.tests.test_risk import vulnerability
7+
8+
9+
@pytest.mark.django_db
10+
def test_simple_risk_pipeline(vulnerability):
11+
pkg = Package.objects.create(type="pypi", name="foo", version="2.3.0")
12+
assert Package.objects.count() == 1
13+
14+
improver = RiskPackagePipeline()
15+
improver.execute()
16+
17+
assert pkg.risk is None
18+
19+
AffectedByPackageRelatedVulnerability.objects.create(package=pkg, vulnerability=vulnerability)
20+
improver = RiskPackagePipeline()
21+
improver.execute()
22+
23+
pkg = Package.objects.get(type="pypi", name="foo", version="2.3.0")
24+
assert str(pkg.risk) == str(3.11)

vulnerabilities/tests/test_api.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,7 @@ def test_api_with_lesser_and_greater_fixed_by_packages(self):
625625
}
626626
],
627627
"resource_url": "http://testserver/packages/pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.13.1",
628+
"risk": None,
628629
}
629630

630631
assert response == expected

vulnerabilities/views.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,6 @@ def get_context_data(self, **kwargs):
123123
context["fixing_vulnerabilities"] = package.fixing.order_by("vulnerability_id")
124124
context["package_search_form"] = PackageSearchForm(self.request.GET)
125125
context["fixed_package_details"] = package.fixed_package_details
126-
context["risk"] = calculate_pkg_risk(package)
127126
context["history"] = list(package.history)
128127
return context
129128

0 commit comments

Comments
 (0)