From 797d59d2fd026acd687063ac973bff90d564c8e7 Mon Sep 17 00:00:00 2001 From: "Chris (stof) Langton" <3494633+chrisdlangton@users.noreply.github.com> Date: Wed, 3 Apr 2024 13:42:48 +1100 Subject: [PATCH 1/8] Update cvss_calculator.py --- cvss/cvss_calculator.py | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/cvss/cvss_calculator.py b/cvss/cvss_calculator.py index c91e14f..54425c5 100755 --- a/cvss/cvss_calculator.py +++ b/cvss/cvss_calculator.py @@ -12,16 +12,18 @@ import argparse import json -from cvss import CVSSError, ask_interactively +from cvss import CVSS2, CVSS3, CVSS4, CVSSError, ask_interactively PAD = 24 # string padding for score names +DEFAULT_VERSION = 3.1 def main(): try: parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument("-2", action="store_true", help="compute CVSS2 instead") - parser.add_argument("-3", action="store_true", help="compute CVSS3.0 instead") + parser.add_argument("-2", action="store_true", help="compute CVSS2 (default {0})".format(DEFAULT_VERSION)) + parser.add_argument("-3", action="store_true", help="compute CVSS3.0 (default {0})".format(DEFAULT_VERSION)) + parser.add_argument("-4", action="store_true", help="compute CVSS4.0 (default {0})".format(DEFAULT_VERSION)) parser.add_argument("-a", "--all", action="store_true", help="ask for all metrics") parser.add_argument("-v", "--vector", help="input string with CVSS vector") parser.add_argument( @@ -32,17 +34,12 @@ def main(): ) args = parser.parse_args() - # Import the correct CVSS module - if getattr(args, "2"): - version = 2 - from cvss import CVSS2 as CVSS - else: - if getattr(args, "3"): - version = 3.0 - else: - version = 3.1 - from cvss import CVSS3 as CVSS - + version_mapping = {"2": 2, "3": 3.0, "3.1": 3.1, "4": 4.0} + # Find the key in args where the value is True + true_version_key = next((key for key, value in args.__dict__.items() if value), None) + # Use the found key to get the version from version_mapping, default to DEFAULT_VERSION if not found + version = version_mapping.get(true_version_key, DEFAULT_VERSION) + print(version) # Vector input, either from command line or interactively if args.vector: vector_string = args.vector @@ -51,7 +48,15 @@ def main(): # Compute scores and clean vector try: - cvss_vector = CVSS(vector_string) + # Init the correct CVSS module + if version == 2: + cvss_vector = CVSS2(vector_string) + elif 3.0 <= version < 4.0: + cvss_vector = CVSS3(vector_string) + elif version == 4.0: + cvss_vector = CVSS4(vector_string) + else: + raise CVSSError("Unknown version: {0}".format(version)) except CVSSError as e: print(e) else: From 7d63564fbbc37acac7d998736d0d67485e865279 Mon Sep 17 00:00:00 2001 From: "Chris (stof) Langton" <3494633+chrisdlangton@users.noreply.github.com> Date: Wed, 3 Apr 2024 13:43:11 +1100 Subject: [PATCH 2/8] Update interactive.py --- cvss/interactive.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/cvss/interactive.py b/cvss/interactive.py index c5503e5..2040926 100644 --- a/cvss/interactive.py +++ b/cvss/interactive.py @@ -32,7 +32,7 @@ def ask_interactively(version=3.1, all_metrics=False, no_colors=False): Asks user to build CVSS vector string interactively. Args: - version (float): 2 or 3.0/3.1 for CVSS2 or CVSS3 respectively + version (float): 2 or 3.0/3.1 or 4 for CVSS2 or CVSS3 or CVSS4 respectively all_metrics (bool): If true, temporal and environmental metrics are asked, else only base metrics are asked for no_colors (bool): If true, terminal coloring is not used in interactive mode @@ -48,13 +48,20 @@ def ask_interactively(version=3.1, all_metrics=False, no_colors=False): METRICS_MANDATORY, METRICS_VALUE_NAMES, ) - elif version >= 3.0: + elif 3.0 <= version < 4.0: print("Interactive CVSS3 calculator") from .constants3 import ( METRICS_ABBREVIATIONS, METRICS_MANDATORY, METRICS_VALUE_NAMES, ) + elif version == 4.0: + print("Interactive CVSS4 calculator") + from .constants4 import ( + METRICS_ABBREVIATIONS, + METRICS_MANDATORY, + METRICS_VALUE_NAMES, + ) else: raise ValueError("Unknown version: {0}".format(version)) print() @@ -117,6 +124,8 @@ def ask_interactively(version=3.1, all_metrics=False, no_colors=False): vector_string = "CVSS:3.0/" + "/".join(vector) elif version == 3.1: vector_string = "CVSS:3.1/" + "/".join(vector) + elif version == 4.0: + vector_string = "CVSS:4.0/" + "/".join(vector) else: vector_string = "/".join(vector) return vector_string From c5f39196874b81b336593d22a09ceef124bf1ff8 Mon Sep 17 00:00:00 2001 From: "Chris (stof) Langton" <3494633+chrisdlangton@users.noreply.github.com> Date: Wed, 3 Apr 2024 13:51:14 +1100 Subject: [PATCH 3/8] Update cvss_calculator.py --- cvss/cvss_calculator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cvss/cvss_calculator.py b/cvss/cvss_calculator.py index 54425c5..fdfb9b2 100755 --- a/cvss/cvss_calculator.py +++ b/cvss/cvss_calculator.py @@ -39,7 +39,7 @@ def main(): true_version_key = next((key for key, value in args.__dict__.items() if value), None) # Use the found key to get the version from version_mapping, default to DEFAULT_VERSION if not found version = version_mapping.get(true_version_key, DEFAULT_VERSION) - print(version) + # Vector input, either from command line or interactively if args.vector: vector_string = args.vector From ff07181d691237134da19c86a8390e71d1e05031 Mon Sep 17 00:00:00 2001 From: "Chris (stof) Langton" <3494633+chrisdlangton@users.noreply.github.com> Date: Wed, 3 Apr 2024 22:58:55 +1100 Subject: [PATCH 4/8] Update cvss_calculator.py --- cvss/cvss_calculator.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/cvss/cvss_calculator.py b/cvss/cvss_calculator.py index fdfb9b2..5ec6772 100755 --- a/cvss/cvss_calculator.py +++ b/cvss/cvss_calculator.py @@ -39,7 +39,6 @@ def main(): true_version_key = next((key for key, value in args.__dict__.items() if value), None) # Use the found key to get the version from version_mapping, default to DEFAULT_VERSION if not found version = version_mapping.get(true_version_key, DEFAULT_VERSION) - # Vector input, either from command line or interactively if args.vector: vector_string = args.vector @@ -61,22 +60,30 @@ def main(): print(e) else: scores = cvss_vector.scores() + severities = None if version == 2: print("CVSS2") - severities = None - elif version >= 3.0: + elif 3.0 <= version < 4.0: print("CVSS3") severities = cvss_vector.severities() + elif version >= 4.0: + print("CVSS4") + severities = cvss_vector.severities() else: raise ValueError("Unknown CVSS version: {0}".format(version)) for i, score_name in enumerate(["Base Score", "Temporal Score", "Environmental Score"]): - print(score_name + ":" + " " * (PAD - len(score_name) - 2), end="") - - if version >= 3.0: - print(scores[i], "({0})".format(severities[i])) - else: - print(scores[i]) + score = None + try: + if version >= 3.0: + score = scores[i], "({0})".format(severities[i]) + else: + score = (scores[i], ) + except IndexError: + pass + if score: + print(score_name + ":" + " " * (PAD - len(score_name) - 2), end="") + print(*score) print("Cleaned vector: ", cvss_vector.clean_vector()) print("Red Hat vector: ", cvss_vector.rh_vector()) if args.json: From e9f7a0c21beb6c943365886948c7e3908d8061f5 Mon Sep 17 00:00:00 2001 From: "Chris (stof) Langton" <3494633+chrisdlangton@users.noreply.github.com> Date: Wed, 3 Apr 2024 22:59:09 +1100 Subject: [PATCH 5/8] Update cvss4.py --- cvss/cvss4.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/cvss/cvss4.py b/cvss/cvss4.py index bd31b5d..739e056 100644 --- a/cvss/cvss4.py +++ b/cvss/cvss4.py @@ -608,6 +608,32 @@ def compute_severity(self): else: self.severity = "Critical" + def scores(self): + """ + Returns computed base score as tuple for backwards compatibility. + + Returns: + (tuple of floats): Base Score + """ + return (self.base_score,) + + def severities(self): + """ + Returns severities based on base score as tuple for backwards compatibility. + + Returns: + (tuple): Base Severity as string + """ + return (self.severity,) + + def rh_vector(self): + """ + Returns cleaned vector with score in Red Hat notation, e.g. score/vector. + + Example: 6.5/CVSS:3.0/AV:P/AC:H/PR:H/UI:R/S:C/C:H/I:H/A:N/E:H/RL:O/RC:R/CR:H/MAC:H/MC:L + """ + return str(self.base_score) + "/" + self.clean_vector() + def as_json(self, sort=False, minimal=False): """ Returns a dictionary formatted with attribute names and values defined by the official From 5604e3cc0de035498bf6477fffcddfbf11a79dfa Mon Sep 17 00:00:00 2001 From: Jakub Svoboda Date: Thu, 4 Apr 2024 11:40:11 +0200 Subject: [PATCH 6/8] Update docstring in cvss/cvss4.py --- cvss/cvss4.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cvss/cvss4.py b/cvss/cvss4.py index 739e056..a6e4c0e 100644 --- a/cvss/cvss4.py +++ b/cvss/cvss4.py @@ -630,7 +630,7 @@ def rh_vector(self): """ Returns cleaned vector with score in Red Hat notation, e.g. score/vector. - Example: 6.5/CVSS:3.0/AV:P/AC:H/PR:H/UI:R/S:C/C:H/I:H/A:N/E:H/RL:O/RC:R/CR:H/MAC:H/MC:L + Example: 7.3/CVSS:4.0/AV:P/AC:H/AT:N/PR:H/UI:P/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H/E:A """ return str(self.base_score) + "/" + self.clean_vector() From f0a371e16355af4948093f0f0fdfa37bdb7cd538 Mon Sep 17 00:00:00 2001 From: Jakub Svoboda Date: Thu, 4 Apr 2024 12:03:14 +0200 Subject: [PATCH 7/8] Reformatted cvss/cvss_calculator.py with `black` --- cvss/cvss_calculator.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/cvss/cvss_calculator.py b/cvss/cvss_calculator.py index 5ec6772..18cbd4b 100755 --- a/cvss/cvss_calculator.py +++ b/cvss/cvss_calculator.py @@ -21,9 +21,15 @@ def main(): try: parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument("-2", action="store_true", help="compute CVSS2 (default {0})".format(DEFAULT_VERSION)) - parser.add_argument("-3", action="store_true", help="compute CVSS3.0 (default {0})".format(DEFAULT_VERSION)) - parser.add_argument("-4", action="store_true", help="compute CVSS4.0 (default {0})".format(DEFAULT_VERSION)) + parser.add_argument( + "-2", action="store_true", help="compute CVSS2 (default {0})".format(DEFAULT_VERSION) + ) + parser.add_argument( + "-3", action="store_true", help="compute CVSS3.0 (default {0})".format(DEFAULT_VERSION) + ) + parser.add_argument( + "-4", action="store_true", help="compute CVSS4.0 (default {0})".format(DEFAULT_VERSION) + ) parser.add_argument("-a", "--all", action="store_true", help="ask for all metrics") parser.add_argument("-v", "--vector", help="input string with CVSS vector") parser.add_argument( @@ -78,7 +84,7 @@ def main(): if version >= 3.0: score = scores[i], "({0})".format(severities[i]) else: - score = (scores[i], ) + score = (scores[i],) except IndexError: pass if score: From 1961720334c5f812e277f338d02d3e21e4771b48 Mon Sep 17 00:00:00 2001 From: Jakub Svoboda Date: Thu, 4 Apr 2024 12:13:57 +0200 Subject: [PATCH 8/8] Break long comment line --- cvss/cvss_calculator.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cvss/cvss_calculator.py b/cvss/cvss_calculator.py index 18cbd4b..18e46e0 100755 --- a/cvss/cvss_calculator.py +++ b/cvss/cvss_calculator.py @@ -43,7 +43,8 @@ def main(): version_mapping = {"2": 2, "3": 3.0, "3.1": 3.1, "4": 4.0} # Find the key in args where the value is True true_version_key = next((key for key, value in args.__dict__.items() if value), None) - # Use the found key to get the version from version_mapping, default to DEFAULT_VERSION if not found + # Use the found key to get the version from version_mapping, + # default to DEFAULT_VERSION if not found. version = version_mapping.get(true_version_key, DEFAULT_VERSION) # Vector input, either from command line or interactively if args.vector: