Skip to content

Release: Merge release into master from: release/2.50.1#13135

Merged
rossops merged 20 commits intomasterfrom
release/2.50.1
Sep 8, 2025
Merged

Release: Merge release into master from: release/2.50.1#13135
rossops merged 20 commits intomasterfrom
release/2.50.1

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 20 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
@rossops rossops closed this Sep 8, 2025
@rossops rossops reopened this Sep 8, 2025
@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 unittests integration_tests labels Sep 8, 2025
@dryrunsecurity
Copy link
Copy Markdown

dryrunsecurity Bot commented Sep 8, 2025

DryRun Security

🔴 Risk threshold exceeded.

This pull request modifies several sensitive codepaths (notably dojo/finding_group/views.py, dojo/test/views.py, and dojo/templates/dojo/view_test.html) flagged by the scanner. It also introduces an information-disclosure/user-enumeration risk in dojo/api_v2/serializers.py (distinct SSO-specific error message) and a potential XSS risk in dojo/tools/semgrep_pro/parser.py where external JSON is interpolated into finding fields without sanitization.

🔴 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/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/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/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 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.
🟡 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

Information Disclosure (User Enumeration) in dojo/api_v2/serializers.py
Vulnerability Information Disclosure (User Enumeration)
Description The UserContactInfoSerializer in dojo/api_v2/serializers.py contains validation logic that raises a specific error message: 'Password resets are not allowed for users authorized through SSO.' when an attempt is made to force a password reset for a user authenticated via Single Sign-On (SSO). This distinct error message, compared to the generic 'Invalid data' or 'Object not found' errors for other scenarios (e.g., non-existent user, user with local password), allows an attacker to differentiate between SSO-managed accounts and other account types. This difference in response can be leveraged to enumerate users who are authenticated through SSO.

model = UserContactInfo
fields = "__all__"
def validate(self, data):
user = data.get("user", None) or self.instance.user
if data.get("force_password_reset", False) and not user.has_usable_password():
msg = "Password resets are not allowed for users authorized through SSO."
raise ValidationError(msg)
return super().validate(data)
class UserStubSerializer(serializers.ModelSerializer):
class Meta:

We've notified @mtesauro.


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

@rossops rossops merged commit 580681e into master Sep 8, 2025
83 checks passed
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