-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Release: Merge release into master from: release/2.48.3 #12829
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
Conversation
….49.0-dev Release: Merge back 2.48.2 into bugfix from: master-into-bugfix/2.48.2-2.49.0-dev
|
This pull request identifies several security and performance concerns, including a potential performance degradation risk in database querying, an inaccurate severity mapping vulnerability in the Trivy parser, and information disclosure risks through debug logging in multiple parsers that could expose sensitive vulnerability details if debug logging is enabled in a production environment.
Potential for Performance Degradation / DoS in
|
| Vulnerability | Potential for Performance Degradation / DoS |
|---|---|
| Description | The open_findings_list method, called by the APIv2 serializer, performs an unbounded database query to fetch all active finding IDs for a product. If a product has a very large number of findings, this operation can consume significant database and application resources, potentially leading to performance degradation or a Denial of Service. |
django-DefectDojo/dojo/models.py
Lines 1322 to 1331 in ddd8df4
| def get_product_type(self): | |
| return self.prod_type if self.prod_type is not None else "unknown" | |
| # only used in APIv2 serializers.py, should be deprecated or at least prefetched | |
| def open_findings_list(self): | |
| findings = Finding.objects.filter(test__engagement__product=self, active=True).values_list("id", flat=True) | |
| return list(findings) | |
| @property | |
| def has_jira_configured(self): |
Potential for Inaccurate Severity Mapping in dojo/tools/trivy/parser.py
| Vulnerability | Potential for Inaccurate Severity Mapping |
|---|---|
| Description | The Trivy parser in dojo/tools/trivy/parser.py processes CVSS scores by first checking the SeveritySource field from the input report. If this field is present, its value is prioritized over a hardcoded list of trusted CVSS sources (nvd, ghsa, redhat, bitnami). The code iterates through [detected_severity_source, *CVSS_SEVERITY_SOURCES] and uses the first matching CVSS data it finds, then breaks the loop. This means if an attacker can control the Trivy report content, they could set SeveritySource to point to a CVSS entry within the report that has a lower score, even if other, more severe, CVSS entries exist for the same vulnerability from trusted sources like NVD. This could lead to a high-severity vulnerability being incorrectly classified with a lower severity in DefectDojo, potentially causing it to be overlooked or deprioritized. |
django-DefectDojo/dojo/tools/trivy/parser.py
Lines 250 to 278 in ddd8df4
| try: | |
| vuln_id = vuln.get("VulnerabilityID", "0") | |
| package_name = vuln["PkgName"] | |
| detected_severity_source = vuln.get("SeveritySource", None) | |
| cvss = vuln.get("CVSS", {}) | |
| cvssclass = None | |
| cvssv3 = None | |
| cvssv3_score = None | |
| # Iterate over the possible severity sources tom find the first match | |
| for severity_source in [detected_severity_source, *CVSS_SEVERITY_SOURCES]: | |
| cvssclass = cvss.get(severity_source, None) | |
| if cvssclass is not None: | |
| break | |
| # Parse the CVSS class if it is not None | |
| if cvssclass is not None: | |
| if cvss_data := parse_cvss_data(cvssclass.get("V3Vector", "")): | |
| cvssv3 = cvss_data.get("vector") | |
| cvssv3_score = cvss_data.get("score") | |
| severity = cvss_data.get("severity") | |
| elif (cvss_v3_score := cvssclass.get("V3Score")) is not None: | |
| cvssv3_score = cvss_v3_score | |
| severity = self.convert_cvss_score(cvss_v3_score) | |
| elif (cvss_v2_score := cvssclass.get("V2Score")) is not None: | |
| severity = self.convert_cvss_score(cvss_v2_score) | |
| else: | |
| severity = self.convert_cvss_score(None) | |
| else: | |
| severity = TRIVY_SEVERITIES[vuln["Severity"]] | |
| if target_class == "os-pkgs" or target_class == "lang-pkgs": |
Information Disclosure via Debug Logging in dojo/tools/anchore_grype/parser.py
| Vulnerability | Information Disclosure via Debug Logging |
|---|---|
| Description | The code adds a logger.debug(f"data: {data}") statement in the Anchore Grype parser. The data variable contains the full parsed JSON content of an Anchore Grype report, which includes sensitive vulnerability details such as CVEs, GHSA IDs, descriptions, CVSS scores, and EPSS scores. If debug logging is enabled in a production environment (e.g., by setting DD_LOG_LEVEL=DEBUG), this sensitive information would be written to the application logs. If these logs are not adequately secured, it could lead to an information disclosure vulnerability, allowing unauthorized access to detailed vulnerability intelligence about the system. |
django-DefectDojo/dojo/tools/anchore_grype/parser.py
Lines 30 to 38 in ddd8df4
| ) | |
| def get_findings(self, file, test): | |
| logger.debug(f"file: {file}") | |
| data = json.load(file) | |
| logger.debug(f"data: {data}") | |
| dupes = {} | |
| for item in data.get("matches", []): | |
| vulnerability = item["vulnerability"] |
Information Disclosure via Debug Logging in dojo/tools/dependency_check/parser.py
| Vulnerability | Information Disclosure via Debug Logging |
|---|---|
| Description | The code logs the full exception object at the debug level (logger.debug(e)). If the DD_DEBUG environment variable is set to True in a production environment, this debug log level will be active, potentially leading to the disclosure of sensitive information such as stack traces, internal file paths, and snippets of processed data (e.g., malformed CVSS strings from vulnerability reports) in the application logs. This information could aid an attacker in understanding the application's internal structure and data handling. |
django-DefectDojo/dojo/tools/dependency_check/parser.py
Lines 213 to 267 in ddd8df4
| return None, None | |
| def get_severity_and_cvss_meta(self, vulnerability, namespace) -> dict: | |
| # Get the base severity from the report | |
| severity = vulnerability.findtext(f"{namespace}severity") | |
| cvssv3 = None | |
| cvssv3_score = None | |
| # Attempt to add the CVSSv3 score, and update the severity accordingly | |
| if (cvssv3_node := vulnerability.find(namespace + "cvssV3")) is not None: | |
| try: | |
| vector_parts = [ | |
| f"AV:{self.CVSS_V3_MAPPINGS['attackVector'][cvssv3_node.findtext(f'{namespace}attackVector')]}", | |
| f"AC:{self.CVSS_V3_MAPPINGS['attackComplexity'][cvssv3_node.findtext(f'{namespace}attackComplexity')]}", | |
| f"PR:{self.CVSS_V3_MAPPINGS['privilegesRequired'][cvssv3_node.findtext(f'{namespace}privilegesRequired')]}", | |
| f"UI:{self.CVSS_V3_MAPPINGS['userInteraction'][cvssv3_node.findtext(f'{namespace}userInteraction')]}", | |
| f"S:{self.CVSS_V3_MAPPINGS['scope'][cvssv3_node.findtext(f'{namespace}scope')]}", | |
| f"C:{self.CVSS_V3_MAPPINGS['confidentialityImpact'][cvssv3_node.findtext(f'{namespace}confidentialityImpact')]}", | |
| f"I:{self.CVSS_V3_MAPPINGS['integrityImpact'][cvssv3_node.findtext(f'{namespace}integrityImpact')]}", | |
| f"A:{self.CVSS_V3_MAPPINGS['availabilityImpact'][cvssv3_node.findtext(f'{namespace}availabilityImpact')]}", | |
| ] | |
| version = cvssv3_node.findtext("version") or "3.1" | |
| vector = f"CVSS:{version}/" + "/".join(vector_parts) | |
| if cvss_data := parse_cvss_data(vector): | |
| cvssv3 = cvss_data.get("vector") | |
| cvssv3_score = cvss_data.get("score") | |
| severity = cvss_data.get("severity") | |
| except Exception as e: | |
| # Only log the error - there is not much we can do to recover from this | |
| logger.debug(e) | |
| elif (cvssv2_node := vulnerability.find(namespace + "cvssV2")) is not None: | |
| severity = cvssv2_node.findtext(f"{namespace}severity").lower().capitalize() | |
| # handle if the severity have something not in the mapping | |
| # default to 'Medium' and produce warnings in logs | |
| if severity: | |
| if severity.strip().lower() not in self.SEVERITY_MAPPING: | |
| logger.warning( | |
| f"Warning: Unknow severity value detected '{severity}'. Bypass to 'Medium' value", | |
| ) | |
| severity = "Medium" | |
| else: | |
| severity = self.SEVERITY_MAPPING[severity.strip().lower()] | |
| else: | |
| severity = "Medium" | |
| return { | |
| "severity": severity, | |
| "cvssv3": cvssv3, | |
| "cvssv3_score": cvssv3_score, | |
| } | |
| def get_finding_from_vulnerability( | |
| self, dependency, related_dependency, vulnerability, test, namespace, | |
| ): |
All finding details can be found in the DryRun Security Dashboard.
Release triggered by
Maffooch