Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for CVSS vectors display #1312

Merged
merged 1 commit into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions vulnerabilities/severity_systems.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ def compute(self, scoring_elements: str) -> str:
"""
return NotImplementedError

def get(self, scoring_elements: str):
return NotImplementedError


@dataclasses.dataclass(order=True)
class Cvssv2ScoringSystem(ScoringSystem):
Expand All @@ -49,6 +52,10 @@ def compute(self, scoring_elements: str) -> str:
"""
return str(CVSS2(vector=scoring_elements).base_score)

def get(self, scoring_elements: str) -> dict:
ziadhany marked this conversation as resolved.
Show resolved Hide resolved
scoring_elements = scoring_elements.strip()
return CVSS2(vector=scoring_elements).as_json()


CVSSV2 = Cvssv2ScoringSystem(
identifier="cvssv2",
Expand All @@ -71,6 +78,10 @@ def compute(self, scoring_elements: str) -> str:
"""
return str(CVSS3(vector=scoring_elements).base_score)

def get(self, scoring_elements: str) -> dict:
scoring_elements = scoring_elements.strip()
return CVSS3(vector=scoring_elements).as_json()


CVSSV3 = Cvssv3ScoringSystem(
identifier="cvssv3",
Expand Down
66 changes: 65 additions & 1 deletion vulnerabilities/templates/vulnerability_details.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{% load humanize %}
{% load widget_tweaks %}
{% load static %}
{% load show_cvss %}

{% block title %}
VulnerableCode Vulnerability Details - {{ vulnerability.vulnerability_id }}
Expand Down Expand Up @@ -52,6 +53,13 @@
</span>
</a>
</li>
<li data-tab="severities-vectors">
<a>
<span>
Severities vectors ({{ severity_vectors|length }})
</span>
</a>
</li>
<li data-tab="history">
<a>
<span>
Expand Down Expand Up @@ -309,7 +317,63 @@
</tbody>
</table>
</div>

<div class="tab-div content" data-content="severities-vectors">
{% for severity_vector in severity_vectors %}
{% if severity_vector.version == '2.0' %}
Vector: {{ severity_vector.vectorString }}
<table class="table is-bordered is-striped is-narrow is-hoverable is-fullwidth gray-header-border">
<tr>
<th>Exploitability (E)</th>
<th>Access Vector (AV)</th>
<th>Access Complexity (AC)</th>
<th>Authentication (Au)</th>
<th>Confidentiality Impact (C)</th>
<th>Integrity Impact (I)</th>
<th>Availability Impact (A)</th>
</tr>
<tr>
<td>{{ severity_vector.exploitability|cvss_printer:"high,functional,unproven,proof_of_concept,not_defined" }}</td>
<td>{{ severity_vector.accessVector|cvss_printer:"local,adjacent_network,network" }}</td>
<td>{{ severity_vector.accessComplexity|cvss_printer:"high,medium,low" }}</td>
<td>{{ severity_vector.authentication|cvss_printer:"multiple,single,none" }}</td>
<td>{{ severity_vector.confidentialityImpact|cvss_printer:"none,partial,complete" }}</td>
<td>{{ severity_vector.integrityImpact|cvss_printer:"none,partial,complete" }}</td>
<td>{{ severity_vector.availabilityImpact|cvss_printer:"none,partial,complete" }}</td>
</tr>
</table>
{% elif severity_vector.version == '3.1' or severity_vector.version == '3.0'%}
Vector: {{ severity_vector.vectorString }}
<table class="table is-bordered is-striped is-narrow is-hoverable is-fullwidth gray-header-border">
<tr>
<th>Attack Vector (AV)</th>
<th>Attack Complexity (AC)</th>
<th>Privileges Required (PR)</th>
<th>User Interaction (UI)</th>
<th>Scope (S)</th>
<th>Confidentiality Impact (C)</th>
<th>Integrity Impact (I)</th>
<th>Availability Impact (A)</th>
</tr>
<tr>
<td>{{ severity_vector.attackVector|cvss_printer:"network,adjacent_network,local,physical"}}</td>
<td>{{ severity_vector.attackComplexity|cvss_printer:"low,high" }}</td>
<td>{{ severity_vector.privilegesRequired|cvss_printer:"none,low,high" }}</td>
<td>{{ severity_vector.userInteraction|cvss_printer:"none,required"}}</td>
<td>{{ severity_vector.scope|cvss_printer:"unchanged,changed" }}</td>
<td>{{ severity_vector.confidentialityImpact|cvss_printer:"high,low,none" }}</td>
<td>{{ severity_vector.integrityImpact|cvss_printer:"high,low,none" }}</td>
<td>{{ severity_vector.availabilityImpact|cvss_printer:"high,low,none" }}</td>
</tr>
</table>
{% endif %}
{% empty %}
<tr>
<td>
There are no known CVSS vectors.
</td>
</tr>
{% endfor %}
</div>
<div class="tab-div content" data-content="history">
<table class="table is-bordered is-striped is-narrow is-hoverable is-fullwidth">
<thead>
Expand Down
8 changes: 8 additions & 0 deletions vulnerabilities/templatetags/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#
# Copyright (c) nexB Inc. and others. All rights reserved.
# VulnerableCode is a trademark of nexB Inc.
# SPDX-License-Identifier: Apache-2.0
# See http://www.apache.org/licenses/LICENSE-2.0 for the license text.
# See https://github.com/nexB/vulnerablecode for support or download.
# See https://aboutcode.org for more information about nexB OSS projects.
#
17 changes: 17 additions & 0 deletions vulnerabilities/templatetags/show_cvss.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from django import template
from django.utils.safestring import mark_safe

register = template.Library()


@register.filter(is_safe=True)
def cvss_printer(selected_vector, vector_values):
"""highlight the selected vector value and return a list of paragraphs"""
p_list = []
selected_vector = selected_vector.lower()
for vector_value in vector_values.split(","):
if selected_vector == vector_value:
p_list.append(f"<p class='has-text-black-bis mb-2'>{selected_vector}</p>")
else:
p_list.append(f"<p class='has-text-grey mb-2'>{vector_value}</p>")
return mark_safe("".join(p_list))
100 changes: 100 additions & 0 deletions vulnerabilities/tests/test_get_serverity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import pytest
from cvss.exceptions import CVSS2MalformedError
from cvss.exceptions import CVSS3MalformedError

from vulnerabilities.severity_systems import CVSSV2
from vulnerabilities.severity_systems import CVSSV3
from vulnerabilities.templatetags.show_cvss import cvss_printer


def test_get_cvss2_vector_values():
assert (
CVSSV2.get("AV:N/AC:L/Au:N/C:P/I:N/A:N ")
== CVSSV2.get("AV:N/AC:L/Au:N/C:P/I:N/A:N")
== {
"accessComplexity": "LOW",
"accessVector": "NETWORK",
"authentication": "NONE",
"availabilityImpact": "NONE",
"availabilityRequirement": "NOT_DEFINED",
"baseScore": 5.0,
"collateralDamagePotential": "NOT_DEFINED",
"confidentialityImpact": "PARTIAL",
"confidentialityRequirement": "NOT_DEFINED",
"environmentalScore": 0.0,
"exploitability": "NOT_DEFINED",
"integrityImpact": "NONE",
"integrityRequirement": "NOT_DEFINED",
"remediationLevel": "NOT_DEFINED",
"reportConfidence": "NOT_DEFINED",
"targetDistribution": "NOT_DEFINED",
"temporalScore": 0.0,
"vectorString": "AV:N/AC:L/Au:N/C:P/I:N/A:N",
"version": "2.0",
}
)

with pytest.raises(CVSS2MalformedError):
CVSSV2.get("")

with pytest.raises(CVSS2MalformedError):
CVSSV2.get("AV:N/AffgL/Au:N/C:P/I:N/A:N ")


def test_get_cvss3_vector_values():
assert (
CVSSV3.get("CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H ")
== CVSSV3.get("CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H")
== {
"attackComplexity": "LOW",
"attackVector": "NETWORK",
"availabilityImpact": "HIGH",
"availabilityRequirement": "NOT_DEFINED",
"baseScore": 9.1,
"baseSeverity": "CRITICAL",
"confidentialityImpact": "HIGH",
"confidentialityRequirement": "NOT_DEFINED",
"environmentalScore": 9.1,
"environmentalSeverity": "CRITICAL",
"exploitCodeMaturity": "NOT_DEFINED",
"integrityImpact": "HIGH",
"integrityRequirement": "NOT_DEFINED",
"modifiedAttackComplexity": "LOW",
"modifiedAttackVector": "NETWORK",
"modifiedAvailabilityImpact": "HIGH",
"modifiedConfidentialityImpact": "HIGH",
"modifiedIntegrityImpact": "HIGH",
"modifiedPrivilegesRequired": "HIGH",
"modifiedScope": "CHANGED",
"modifiedUserInteraction": "NONE",
"privilegesRequired": "HIGH",
"remediationLevel": "NOT_DEFINED",
"reportConfidence": "NOT_DEFINED",
"scope": "CHANGED",
"temporalScore": 9.1,
"temporalSeverity": "CRITICAL",
"userInteraction": "NONE",
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H",
"version": "3.1",
}
)

with pytest.raises(CVSS3MalformedError):
CVSSV3.get("CVSS:3.7/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H ")

with pytest.raises(CVSS3MalformedError):
CVSSV3.get("")


def test_blank_cvss_printer():
result = cvss_printer("", "")
assert result == "<p class='has-text-black-bis mb-2'></p>"


def test_cvss_printer():
result = cvss_printer("HIGH", "high,medium,low")
assert result == (
"<p class='has-text-black-bis mb-2'>high</p>"
"<p class='has-text-grey mb-2'>medium</p>"
"<p class='has-text-grey mb-2'>low</p>"
)
16 changes: 15 additions & 1 deletion vulnerabilities/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
# See https://github.com/nexB/vulnerablecode for support or download.
# See https://aboutcode.org for more information about nexB OSS projects.
#

import logging
from datetime import datetime

from cvss.exceptions import CVSS2MalformedError
from cvss.exceptions import CVSS3MalformedError
from django.contrib import messages
from django.core.exceptions import ValidationError
from django.core.mail import send_mail
Expand All @@ -26,6 +28,7 @@
from vulnerabilities.forms import PackageSearchForm
from vulnerabilities.forms import VulnerabilitySearchForm
from vulnerabilities.models import VulnerabilityStatusType
from vulnerabilities.severity_systems import SCORING_SYSTEMS
from vulnerabilities.utils import get_severity_range
from vulnerablecode.settings import env

Expand Down Expand Up @@ -132,6 +135,16 @@ def get_context_data(self, **kwargs):
weakness_object for weakness_object in weaknesses if weakness_object.weakness
]
status = self.object.get_status_label

severity_vectors = []
for s in self.object.severities:
if s.scoring_elements and s.scoring_system in SCORING_SYSTEMS:
try:
vector_values = SCORING_SYSTEMS[s.scoring_system].get(s.scoring_elements)
severity_vectors.append(vector_values)
except (CVSS2MalformedError, CVSS3MalformedError, NotImplementedError):
logging.error(f"CVSSMalformedError for {s.scoring_elements}")

context.update(
{
"vulnerability": self.object,
Expand All @@ -140,6 +153,7 @@ def get_context_data(self, **kwargs):
"severity_score_range": get_severity_range(
{s.value for s in self.object.severities}
),
"severity_vectors": severity_vectors,
"references": self.object.references.all(),
"aliases": self.object.aliases.all(),
"affected_packages": self.object.affected_packages.all(),
Expand Down
Loading