|
2 | 2 | import re
|
3 | 3 | from json.decoder import JSONDecodeError
|
4 | 4 |
|
5 |
| -import cvss.parser |
6 |
| -from cvss import CVSS2, CVSS3 |
| 5 | +# import cvss.parser |
| 6 | +from cvss import CVSS2, CVSS3, CVSS4, CVSSError |
7 | 7 |
|
8 | 8 | from dojo.models import Finding
|
9 | 9 |
|
10 | 10 |
|
| 11 | +# TEMPORARY: Local implementation until the upstream PR is merged & released: https://github.com/RedHatProductSecurity/cvss/pull/75 |
| 12 | +def parse_cvss_from_text(text): |
| 13 | + """ |
| 14 | + Parses CVSS2, CVSS3, and CVSS4 vectors from arbitrary text and returns a list of CVSS objects. |
| 15 | +
|
| 16 | + Parses text for substrings that look similar to CVSS vector |
| 17 | + and feeds these matches to CVSS constructor. |
| 18 | +
|
| 19 | + Args: |
| 20 | + text (str): arbitrary text |
| 21 | +
|
| 22 | + Returns: |
| 23 | + A list of CVSS objects. |
| 24 | +
|
| 25 | + """ |
| 26 | + # Looks for substrings that resemble CVSS2, CVSS3, or CVSS4 vectors. |
| 27 | + # CVSS3 and CVSS4 vectors start with a 'CVSS:x.x/' prefix and are matched by the optional non-capturing group. |
| 28 | + # CVSS2 vectors do not include a prefix and are matched by raw vector pattern only. |
| 29 | + # Minimum total match length is 26 characters to reduce false positives. |
| 30 | + matches = re.compile(r"(?:CVSS:[3-4]\.\d/)?[A-Za-z:/]{26,}").findall(text) |
| 31 | + |
| 32 | + cvsss = set() |
| 33 | + for match in matches: |
| 34 | + try: |
| 35 | + if match.startswith("CVSS:4."): |
| 36 | + cvss = CVSS4(match) |
| 37 | + elif match.startswith("CVSS:3."): |
| 38 | + cvss = CVSS3(match) |
| 39 | + else: |
| 40 | + cvss = CVSS2(match) |
| 41 | + |
| 42 | + cvsss.add(cvss) |
| 43 | + except (CVSSError, KeyError): |
| 44 | + pass |
| 45 | + |
| 46 | + return list(cvsss) |
| 47 | + |
| 48 | + |
11 | 49 | class AuditJSParser:
|
12 | 50 |
|
13 | 51 | """Parser for AuditJS Scan tool"""
|
@@ -85,33 +123,38 @@ def get_findings(self, filename, test):
|
85 | 123 | if cwe_find:
|
86 | 124 | cwe = int(cwe_find[0][4:])
|
87 | 125 | else:
|
88 |
| - msg = ( |
89 |
| - "Missing mandatory attributes (id, title, description). Please check your report or ask " |
90 |
| - "community." |
91 |
| - ) |
| 126 | + msg = "Missing mandatory attributes (id, title, description). Please check your report or ask community." |
92 | 127 | raise ValueError(msg)
|
93 | 128 | if "cvssScore" in vulnerability:
|
94 | 129 | cvss_score = vulnerability["cvssScore"]
|
95 | 130 | if "cvssVector" in vulnerability:
|
96 |
| - cvss_vectors = cvss.parser.parse_cvss_from_text( |
| 131 | + cvss_vectors = parse_cvss_from_text( |
97 | 132 | vulnerability["cvssVector"],
|
98 | 133 | )
|
99 |
| - if len(cvss_vectors) > 0 and isinstance( |
100 |
| - cvss_vectors[0], CVSS3, |
101 |
| - ): |
102 |
| - # Only set finding vector if it's version 3 |
103 |
| - cvss_vector = cvss_vectors[0].clean_vector() |
104 |
| - severity = cvss_vectors[0].severities()[0] |
105 |
| - elif len(cvss_vectors) > 0 and isinstance( |
106 |
| - cvss_vectors[0], CVSS2, |
107 |
| - ): |
108 |
| - # Otherwise add it to description |
109 |
| - description = ( |
110 |
| - description |
111 |
| - + "\nCVSS V2 Vector:" |
112 |
| - + cvss_vectors[0].clean_vector() |
113 |
| - ) |
114 |
| - severity = cvss_vectors[0].severities()[0] |
| 134 | + |
| 135 | + if len(cvss_vectors) > 0: |
| 136 | + vector_obj = cvss_vectors[0] |
| 137 | + |
| 138 | + if isinstance(vector_obj, CVSS4): |
| 139 | + description += "\nCVSS V4 Vector:" + vector_obj.clean_vector() |
| 140 | + severity = vector_obj.severities()[0] |
| 141 | + |
| 142 | + elif isinstance(vector_obj, CVSS3): |
| 143 | + cvss_vector = vector_obj.clean_vector() |
| 144 | + severity = vector_obj.severities()[0] |
| 145 | + |
| 146 | + elif isinstance(vector_obj, CVSS2): |
| 147 | + description += "\nCVSS V2 Vector:" + vector_obj.clean_vector() |
| 148 | + severity = vector_obj.severities()[0] |
| 149 | + |
| 150 | + else: |
| 151 | + msg = "Unsupported CVSS version detected in parser." |
| 152 | + raise ValueError(msg) |
| 153 | + else: |
| 154 | + # Explicitly raise an error if no CVSS vectors are found, |
| 155 | + # to avoid 'NoneType' errors during severity processing later. |
| 156 | + msg = "No CVSS vectors found. Please check that parse_cvss_from_text() correctly parses the provided cvssVector." |
| 157 | + raise ValueError(msg) |
115 | 158 | else:
|
116 | 159 | # If there is no vector, calculate severity based on
|
117 | 160 | # score and CVSS V3 (AuditJS does not always include
|
|
0 commit comments