Skip to content

Release: Merge back 2.50.1 into dev from: master-into-dev/2.50.1-2.51.0-dev#13138

Merged
rossops merged 23 commits intodevfrom
master-into-dev/2.50.1-2.51.0-dev
Sep 8, 2025
Merged

Release: Merge back 2.50.1 into dev from: master-into-dev/2.50.1-2.51.0-dev#13138
rossops merged 23 commits intodevfrom
master-into-dev/2.50.1-2.51.0-dev

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot commented Sep 8, 2025

Release triggered by rossops

DefectDojo release bot and others added 22 commits September 2, 2025 15:00
….51.0-dev

Release: Merge back 2.50.0 into bugfix from: master-into-bugfix/2.50.0-2.51.0-dev
* 🎉 Add fix_available to KrakenDAudit

* Update settings.dist.py
* 🎉 Add number of fix_available information to test view

* Update dojo/templates/dojo/view_test.html

Co-authored-by: valentijnscholten <valentijnscholten@gmail.com>

---------

Co-authored-by: Cody Maffucci <46459665+Maffooch@users.noreply.github.com>
Co-authored-by: valentijnscholten <valentijnscholten@gmail.com>
Co-authored-by: Jino Tesauro <jino@defectdojo.com>
* add about_deduplication png

* update changelog 2.50

* update changelog 2.50

---------

Co-authored-by: Paul Osinski <paul.m.osinski@gmail.com>
* semgrep pro: parse sast finding

* update docs
* 🐛 Implement Wazuh v4.8

* update unittests

* update

* fix

* fix

* fix

* update unittests

* update

* fix unittest

* review
* 🐛 Fix finding_group view

* ruff

* finding group view: add basic UI tests

---------

Co-authored-by: Valentijn Scholten <valentijnscholten@gmail.com>
fix default order to finding_groups
Release: Merge release into master from: release/2.50.1
@dryrunsecurity
Copy link
Copy Markdown

dryrunsecurity Bot commented Sep 8, 2025

DryRun Security

🔴 Risk threshold exceeded.

This pull request modifies multiple sensitive codepaths (views, templates, serializers, forms, and a docker entrypoint) flagged by the scanner and contains several non-blocking findings. Specific issues to review: potential XSS in dojo/tools/semgrep_pro/parser.py where external JSON is interpolated without sanitization, a possible CSV-based DoS in dojo/tools/qualys_hacker_guardian/parser.py due to an extremely large csv.field_size_limit, and an information-disclosure concern in dojo/forms.py that reveals users' SSO status.

🔴 Configured Codepaths Edit in dojo/finding_group/views.py
Vulnerability Configured Codepaths Edit
Description Sensitive edits detected for this file. Sensitive file paths and allowed authors can be configured in .dryrunsecurity.yaml.
🔴 Configured Codepaths Edit in dojo/templates/dojo/view_test.html
Vulnerability Configured Codepaths Edit
Description Sensitive edits detected for this file. Sensitive file paths and allowed authors can be configured in .dryrunsecurity.yaml.
🔴 Configured Codepaths Edit in dojo/finding_group/views.py
Vulnerability Configured Codepaths Edit
Description Sensitive edits detected for this file. Sensitive file paths and allowed authors can be configured in .dryrunsecurity.yaml.
🔴 Configured Codepaths Edit in dojo/finding_group/views.py
Vulnerability Configured Codepaths Edit
Description Sensitive edits detected for this file. Sensitive file paths and allowed authors can be configured in .dryrunsecurity.yaml.
🔴 Configured Codepaths Edit in docker/entrypoint-uwsgi.sh
Vulnerability Configured Codepaths Edit
Description Sensitive edits detected for this file. Sensitive file paths and allowed authors can be configured in .dryrunsecurity.yaml.
🔴 Configured Codepaths Edit in dojo/api_v2/serializers.py
Vulnerability Configured Codepaths Edit
Description Sensitive edits detected for this file. Sensitive file paths and allowed authors can be configured in .dryrunsecurity.yaml.
🔴 Configured Codepaths Edit in dojo/finding_group/views.py
Vulnerability Configured Codepaths Edit
Description Sensitive edits detected for this file. Sensitive file paths and allowed authors can be configured in .dryrunsecurity.yaml.
🔴 Configured Codepaths Edit in dojo/forms.py
Vulnerability Configured Codepaths Edit
Description Sensitive edits detected for this file. Sensitive file paths and allowed authors can be configured in .dryrunsecurity.yaml.
🔴 Configured Codepaths Edit in dojo/templates/dojo/view_test.html
Vulnerability Configured Codepaths Edit
Description Sensitive edits detected for this file. Sensitive file paths and allowed authors can be configured in .dryrunsecurity.yaml.
🔴 Configured Codepaths Edit in dojo/test/views.py
Vulnerability Configured Codepaths Edit
Description Sensitive edits detected for this file. Sensitive file paths and allowed authors can be configured in .dryrunsecurity.yaml.
🔴 Configured Codepaths Edit in dojo/user/views.py
Vulnerability Configured Codepaths Edit
Description Sensitive edits detected for this file. Sensitive file paths and allowed authors can be configured in .dryrunsecurity.yaml.
🔴 Configured Codepaths Edit in dojo/api_v2/serializers.py
Vulnerability Configured Codepaths Edit
Description Sensitive edits detected for this file. Sensitive file paths and allowed authors can be configured in .dryrunsecurity.yaml.
🔴 Configured Codepaths Edit in dojo/test/views.py
Vulnerability Configured Codepaths Edit
Description Sensitive edits detected for this file. Sensitive file paths and allowed authors can be configured in .dryrunsecurity.yaml.
🔴 Configured Codepaths Edit in dojo/test/views.py
Vulnerability Configured Codepaths Edit
Description Sensitive edits detected for this file. Sensitive file paths and allowed authors can be configured in .dryrunsecurity.yaml.
🔴 Configured Codepaths Edit in dojo/finding_group/views.py
Vulnerability Configured Codepaths Edit
Description Sensitive edits detected for this file. Sensitive file paths and allowed authors can be configured in .dryrunsecurity.yaml.
🔴 Configured Codepaths Edit in dojo/user/views.py
Vulnerability Configured Codepaths Edit
Description Sensitive edits detected for this file. Sensitive file paths and allowed authors can be configured in .dryrunsecurity.yaml.
🟡 Potential Cross-Site Scripting in dojo/tools/semgrep_pro/parser.py
Vulnerability Potential Cross-Site Scripting
Description File dojo/tools/semgrep_pro/parser.py directly loads external JSON via json.load(filename) and then interpolates numerous fields from that JSON into Finding attributes (description, mitigation, references, impact, etc.) without any sanitization or escaping. Examples in the patch: desc += f"Message: {item['rule_message']}\n\n" (in get_description), mitigation_parts.append(f"Suggested Fix:\n

import contextlib
import json
from datetime import datetime
from dojo.models import Finding
class SemgrepProParser:
def get_scan_types(self):
return ["Semgrep Pro JSON Report"]
def get_label_for_scan_types(self, scan_type):
return scan_type
def get_description_for_scan_types(self, scan_type):
return "Import Semgrep Pro findings in JSON format"
def get_findings(self, filename, test):
data = json.load(filename)
dupes = {}
for item in data.get("findings", []):
# Ensure required fields have default values
title = item.get("rule_name", "No title")
file_path = item.get("location", {}).get("file_path", "")
line = item.get("location", {}).get("line", 0)
# Map status to active/verified
status = item.get("status", "new").lower()
active = status not in {"fixed", "removed"}
triage_status = item.get("triage_state", "untriaged").lower()
verified = triage_status != "untriaged"
finding = Finding(
test=test,
title=title,
severity=self.convert_severity(item.get("severity", "INFO")),
description=self.get_description(item),
file_path=file_path,
line=line,
static_finding=True,
dynamic_finding=False,
vuln_id_from_tool=item.get("rule_name"),
nb_occurences=1,
active=active,
verified=verified,
)
# Add CWE if available
if "rule" in item and "cwe_names" in item["rule"]:
try:
cwe_name = item["rule"]["cwe_names"][0] # Take first CWE
finding.cwe = int(cwe_name.split("-")[1].split(":")[0])
except (ValueError, IndexError, KeyError):
finding.cwe = None
# Add references if available
references = []
if "line_of_code_url" in item:
references.append(f"Line of Code: {item['line_of_code_url']}")
if "rule" in item:
if "owasp_names" in item["rule"]:
references.extend(item["rule"]["owasp_names"])
if "cwe_names" in item["rule"]:
references.extend(item["rule"]["cwe_names"])
if "external_ticket" in item:
references.append(f"External Ticket: \n {item['external_ticket']}")
# Add file location details
if references:
finding.references = "\n".join(references)
# Add mitigation if available
mitigation_parts = []
if "assistant" in item:
assistant = item["assistant"]
if "guidance" in assistant:
if "summary" in assistant["guidance"]:
mitigation_parts.append(f"**Guidance Summary:**\n{assistant['guidance']['summary']}")
if "instructions" in assistant["guidance"]:
mitigation_parts.append(f"**Instructions:**\n{assistant['guidance']['instructions']}")
if "autofix" in assistant:
autofix = assistant["autofix"]
if "fix_code" in autofix:
mitigation_parts.append(f"**Suggested Fix:**\n```\n{autofix['fix_code']}\n```")
if autofix.get("explanation"):
mitigation_parts.append(f"**Fix Explanation:**\n{autofix['explanation']}")
if "autotriage" in assistant:
autotriage = assistant["autotriage"]
if "verdict" in autotriage:
mitigation_parts.append(f"**Auto-triage Verdict:** {autotriage['verdict']}")
if "reason" in autotriage:
mitigation_parts.append(f"**Auto-triage Reason:** {autotriage['reason']}")
if "component" in assistant:
component = assistant["component"]
if "tag" in component:
mitigation_parts.append(f"**Component:** {component['tag']}")
if "risk" in component:
mitigation_parts.append(f"**Risk Level:** {component['risk']}")
finding.mitigation = "\n\n".join(mitigation_parts) if mitigation_parts else None
# Add unique identifier
finding.unique_id_from_tool = item.get("match_based_id")
# Add component name and version if available
if "assistant" in item and "component" in item["assistant"]:
finding.component_name = item["assistant"]["component"].get("tag")
# Add dates
if "created_at" in item:
with contextlib.suppress(ValueError, TypeError):
finding.date = datetime.strptime(item["created_at"].split(".")[0], "%Y-%m-%dT%H:%M:%S")
# Add impact
impact_parts = []
if "rule" in item and "vulnerability_classes" in item["rule"]:
impact_parts.extend(item["rule"]["vulnerability_classes"])
if "confidence" in item:
impact_parts.append(f"Confidence: {item['confidence'].capitalize()}")
if "repository" in item:
repo = item["repository"]
impact_parts.append(f"Repository: {repo.get('name', '')} ({repo.get('url', '')})")
finding.impact = "\n".join(impact_parts)
# Use match_based_id for deduplication if available, otherwise use file location
dupe_key = finding.unique_id_from_tool or title + str(file_path) + str(line)
if dupe_key in dupes:
dupes[dupe_key].nb_occurences += 1
else:
dupes[dupe_key] = finding
return list(dupes.values())
def convert_severity(self, val):
val = val.upper()
if val in {"ERROR", "HIGH"}:
return "High"
if val in {"WARNING", "MEDIUM"}:
return "Medium"
if val in {"INFO", "LOW"}:
return "Low"
if val == "CRITICAL":
return "Critical"
return "Info"
def get_description(self, item):
desc = ""
if "rule_message" in item:
desc += f"**Message:** {item['rule_message']}\n\n"
if "rule" in item:
if "message" in item["rule"]:
desc += f"**Rule Message:** {item['rule']['message']}\n\n"
if "category" in item["rule"]:
desc += f"**Category:** {item['rule']['category']}\n\n"
if "confidence" in item["rule"]:
desc += f"**Confidence:** {item['rule']['confidence']}\n\n"
if "vulnerability_classes" in item["rule"]:
desc += "**Vulnerability Classes:**\n"
for vuln_class in item["rule"]["vulnerability_classes"]:
desc += f"- {vuln_class}\n"
desc += "\n"
if "cwe_names" in item["rule"]:
desc += "**CWE References:**\n"
for cwe in item["rule"]["cwe_names"]:
desc += f"- {cwe}\n"
desc += "\n"
if "owasp_names" in item["rule"]:
desc += "**OWASP References:**\n"
for owasp in item["rule"]["owasp_names"]:
desc += f"- {owasp}\n"
desc += "\n"
# Add categories
if "categories" in item:
desc += "**Categories:**\n"
for category in item["categories"]:
desc += f"- {category}\n"
desc += "\n"
# Add triage information
if "triage_state" in item:
desc += f"**Triage State:** {item['triage_state']}\n"
if "triage_comment" in item:
desc += f"**Triage Comment:** {item['triage_comment']}\n"
if "triage_reason" in item:
desc += f"**Triage Reason:** {item['triage_reason']}\n\n"
return desc

Denial of Service (DoS) via Excessive CSV Field Size in dojo/tools/qualys_hacker_guardian/parser.py
Vulnerability Denial of Service (DoS) via Excessive CSV Field Size
Description The code sets the csv.field_size_limit to an extremely large value (sys.maxsize / 10). This allows a single field within a CSV file to consume an arbitrary amount of memory, limited only by the system's available RAM. An attacker can craft a malicious CSV file with a single, very long field, causing the server to attempt to allocate an excessive amount of memory during parsing, leading to memory exhaustion and a Denial of Service (DoS).

content = filename.read()
if isinstance(content, bytes):
content = content.decode("utf-8")
csv.field_size_limit(int(sys.maxsize / 10))
reader = csv.DictReader(io.StringIO(content), delimiter=",", quotechar='"')
dupes = {}
for row in reader:

Information Disclosure in dojo/forms.py
Vulnerability Information Disclosure
Description The UserContactInfoForm displays a help text 'This user is authorized through SSO, and does not have a password to reset' if the user being edited or viewed does not have a usable password (typically indicating SSO authentication). This information is exposed on the user's profile page (/profile) and the user edit page (/user/<uid>/edit). While the view_profile page is for a user to view their own profile, the edit_user page allows privileged users to edit other users' profiles. The edit_user function has a permission check (user_has_permission_or_403(request, 'dojo.change_user')). This means any user with dojo.change_user permission can view this information for any other user, including administrators. Knowing that a high-privileged user authenticates via SSO could be valuable for an attacker in a social engineering or targeted attack, as it narrows down potential attack vectors (e.g., no password brute-forcing, focus on phishing SSO credentials).

exclude = ["user", "slack_user_id"]
def __init__(self, *args, **kwargs):
user = kwargs.pop("user", None)
super().__init__(*args, **kwargs)
# Do not expose force password reset if the current user does not have a password to reset
if user is not None:
if not user.has_usable_password():
self.fields["force_password_reset"].disabled = True
self.fields["force_password_reset"].help_text = "This user is authorized through SSO, and does not have a password to reset"
# Determine some other settings based on the current user
current_user = get_current_user()
if not current_user.is_superuser:
if not user_has_configuration_permission(current_user, "auth.change_user") and \

We've notified @mtesauro.


All finding details can be found in the DryRun Security Dashboard.

@github-actions github-actions Bot added docker settings_changes Needs changes to settings.py based on changes in settings.dist.py included in this PR apiv2 docs labels Sep 8, 2025
@rossops rossops merged commit f42df63 into dev Sep 8, 2025
86 checks passed
@rossops rossops deleted the master-into-dev/2.50.1-2.51.0-dev branch September 8, 2025 18:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

apiv2 docker docs helm integration_tests parser settings_changes Needs changes to settings.py based on changes in settings.dist.py included in this PR ui unittests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants