From a385129efc39a30544ef892bcf6cce59e503e944 Mon Sep 17 00:00:00 2001 From: Valentijn Scholten Date: Sun, 24 Aug 2025 10:29:41 +0200 Subject: [PATCH 1/2] cvss4: remove no longer needed custom parsing --- dojo/utils.py | 40 ++++------------------------------------ 1 file changed, 4 insertions(+), 36 deletions(-) diff --git a/dojo/utils.py b/dojo/utils.py index dc393babeca..f2128f11409 100644 --- a/dojo/utils.py +++ b/dojo/utils.py @@ -21,7 +21,8 @@ from auditlog.models import LogEntry from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes -from cvss import CVSS2, CVSS3, CVSS4, CVSSError +from cvss import CVSS2, CVSS3, CVSS4 +from cvss import parse_cvss_from_text as cvss_parse_cvss_from_text from dateutil.parser import parse from dateutil.relativedelta import MO, SU, relativedelta from django.conf import settings @@ -2660,42 +2661,9 @@ def generate_file_response_from_file_path( return response -# TEMPORARY: Local implementation until the upstream PR is merged & released: https://github.com/RedHatProductSecurity/cvss/pull/75 +# used to add some custom logic, but that's now present in cvss 3.6. might be good to retain our own wrapper just in case/for now def parse_cvss_from_text(text): - """ - Parses CVSS2, CVSS3, and CVSS4 vectors from arbitrary text and returns a list of CVSS objects. - - Parses text for substrings that look similar to CVSS vector - and feeds these matches to CVSS constructor. - - Args: - text (str): arbitrary text - - Returns: - A list of CVSS objects. - - """ - # Looks for substrings that resemble CVSS2, CVSS3, or CVSS4 vectors. - # CVSS3 and CVSS4 vectors start with a 'CVSS:x.x/' prefix and are matched by the optional non-capturing group. - # CVSS2 vectors do not include a prefix and are matched by raw vector pattern only. - # Minimum total match length is 26 characters to reduce false positives. - matches = re.compile(r"(?:CVSS:[3-4]\.\d/)?[A-Za-z:/]{26,}").findall(text) - - cvsss = set() - for match in matches: - try: - if match.startswith("CVSS:4."): - cvss = CVSS4(match) - elif match.startswith("CVSS:3."): - cvss = CVSS3(match) - else: - cvss = CVSS2(match) - - cvsss.add(cvss) - except (CVSSError, KeyError): - pass - - return list(cvsss) + return cvss_parse_cvss_from_text(text) def parse_cvss_data(cvss_vector_string: str) -> dict: From a45eca4d93733888357338c88102a9b656ed0b8f Mon Sep 17 00:00:00 2001 From: Valentijn Scholten Date: Sun, 24 Aug 2025 12:54:52 +0200 Subject: [PATCH 2/2] cvss4: remove no longer needed custom parsing --- .../en/open_source/contributing/how-to-write-a-parser.md | 2 +- dojo/utils.py | 9 ++------- dojo/validators.py | 7 +++---- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/docs/content/en/open_source/contributing/how-to-write-a-parser.md b/docs/content/en/open_source/contributing/how-to-write-a-parser.md index 26fea0bc240..3c0fcfe53a9 100644 --- a/docs/content/en/open_source/contributing/how-to-write-a-parser.md +++ b/docs/content/en/open_source/contributing/how-to-write-a-parser.md @@ -197,7 +197,7 @@ Example of use: from cvss import CVSS2, CVSS3, CVSS4 # TEMPORARY: Use Defect Dojo implementation of `parse_cvss_from_text` white waiting for https://github.com/RedHatProductSecurity/cvss/pull/75 to be released - vectors = dojo.utils.parse_cvss_from_text("CVSS:3.0/S:C/C:H/I:H/A:N/AV:P/AC:H/PR:H/UI:R/E:H/RL:O/RC:R/CR:H/IR:X/AR:X/MAC:H/MPR:X/MUI:X/MC:L/MA:X") + vectors = cvss.parser.parse_cvss_from_text("CVSS:3.0/S:C/C:H/I:H/A:N/AV:P/AC:H/PR:H/UI:R/E:H/RL:O/RC:R/CR:H/IR:X/AR:X/MAC:H/MPR:X/MUI:X/MC:L/MA:X") if len(vectors) > 0 and type(vectors[0]) is CVSS3: print(vectors[0].severities()) # this is the 3 severities diff --git a/dojo/utils.py b/dojo/utils.py index f2128f11409..991b6b84075 100644 --- a/dojo/utils.py +++ b/dojo/utils.py @@ -15,6 +15,7 @@ import bleach import crum +import cvss import hyperlink import vobject from asteval import Interpreter @@ -22,7 +23,6 @@ from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cvss import CVSS2, CVSS3, CVSS4 -from cvss import parse_cvss_from_text as cvss_parse_cvss_from_text from dateutil.parser import parse from dateutil.relativedelta import MO, SU, relativedelta from django.conf import settings @@ -2661,16 +2661,11 @@ def generate_file_response_from_file_path( return response -# used to add some custom logic, but that's now present in cvss 3.6. might be good to retain our own wrapper just in case/for now -def parse_cvss_from_text(text): - return cvss_parse_cvss_from_text(text) - - def parse_cvss_data(cvss_vector_string: str) -> dict: if not cvss_vector_string: return {} - vectors = parse_cvss_from_text(cvss_vector_string) + vectors = cvss.parser.parse_cvss_from_text(cvss_vector_string) if len(vectors) > 0: vector = vectors[0] # For CVSS2, environmental score is at index 2 diff --git a/dojo/validators.py b/dojo/validators.py index 8c5dc94d7de..aa3c4e5da5c 100644 --- a/dojo/validators.py +++ b/dojo/validators.py @@ -2,6 +2,7 @@ import re from collections.abc import Callable +import cvss from cvss import CVSS2, CVSS3, CVSS4 from django.core.exceptions import ValidationError from django.core.validators import FileExtensionValidator @@ -49,8 +50,7 @@ def clean_tags(value: str | list[str], exception_class: Callable = ValidationErr def cvss3_validator(value: str | list[str], exception_class: Callable = ValidationError) -> None: logger.debug("cvss3_validator called with value: %s", value) - from dojo.utils import parse_cvss_from_text - cvss_vectors = parse_cvss_from_text(value) + cvss_vectors = cvss.parser.parse_cvss_from_text(value) if len(cvss_vectors) > 0: vector_obj = cvss_vectors[0] @@ -76,8 +76,7 @@ def cvss3_validator(value: str | list[str], exception_class: Callable = Validati def cvss4_validator(value: str | list[str], exception_class: Callable = ValidationError) -> None: logger.debug("cvss4_validator called with value: %s", value) - from dojo.utils import parse_cvss_from_text - cvss_vectors = parse_cvss_from_text(value) + cvss_vectors = cvss.parser.parse_cvss_from_text(value) if len(cvss_vectors) > 0: vector_obj = cvss_vectors[0]