Skip to content

Commit

Permalink
Merge pull request #10194 from DefectDojo/release/2.34.3
Browse files Browse the repository at this point in the history
Release: Merge release into master from: release/2.34.3
  • Loading branch information
Maffooch authored May 13, 2024
2 parents 2c7b506 + a97f3b3 commit 2234848
Show file tree
Hide file tree
Showing 19 changed files with 149 additions and 22 deletions.
2 changes: 1 addition & 1 deletion components/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "defectdojo",
"version": "2.34.2",
"version": "2.34.3",
"license" : "BSD-3-Clause",
"private": true,
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion docs/content/en/integrations/parsers/api/sonarqube.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ only one defined or the SonarQube `Tool Configuration` if there is only one.
## Multi Branch Scanning

If using a version of SonarQube with multi branch scanning, the branch tha be scanned can
be supplied in the `branch tag` fieild at import/re-import time. If the branch does not exist,
be supplied in the `branch_tag` fieild at import/re-import time. If the branch does not exist,
a notification will be generated in the alerts table indicating that branch to be imported
does not exist. If a branch name is not supplied during import/re-import, the default branch
of the SonarQube project will be used.
Expand Down
2 changes: 1 addition & 1 deletion dojo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
# Django starts so that shared_task will use this app.
from .celery import app as celery_app # noqa: F401

__version__ = '2.34.2'
__version__ = '2.34.3'
__url__ = 'https://github.com/DefectDojo/django-DefectDojo'
__docs__ = 'https://documentation.defectdojo.com'
19 changes: 19 additions & 0 deletions dojo/api_v2/serializers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import json
import logging
import os
import re
from datetime import datetime
from typing import List
Expand Down Expand Up @@ -797,6 +798,24 @@ class Meta:
model = FileUpload
fields = "__all__"

def validate(self, data):
if file := data.get("file"):
ext = os.path.splitext(file.name)[1] # [0] returns path+filename
valid_extensions = settings.FILE_UPLOAD_TYPES
if ext.lower() not in valid_extensions:
if accepted_extensions := f"{', '.join(valid_extensions)}":
msg = (
"Unsupported extension. Supported extensions are as "
f"follows: {accepted_extensions}"
)
else:
msg = (
"File uploads are prohibited due to the list of acceptable "
"file extensions being empty"
)
raise ValidationError(msg)
return data


class RawFileSerializer(serializers.ModelSerializer):
file = serializers.FileField(required=True)
Expand Down
13 changes: 11 additions & 2 deletions dojo/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -850,13 +850,22 @@ def clean(self):
# Don't bother validating the formset unless each form is valid on its own
return
for form in self.forms:
print(dir(form))
file = form.cleaned_data.get('file', None)
if file:
ext = os.path.splitext(file.name)[1] # [0] returns path+filename
valid_extensions = settings.FILE_UPLOAD_TYPES
if ext.lower() not in valid_extensions:
form.add_error('file', 'Unsupported file extension.')
if accepted_extensions := f"{', '.join(valid_extensions)}":
msg = (
"Unsupported extension. Supported extensions are as "
f"follows: {accepted_extensions}"
)
else:
msg = (
"File uploads are prohibited due to the list of acceptable "
"file extensions being empty"
)
form.add_error('file', msg)


ManageFileFormSet = modelformset_factory(FileUpload, extra=3, max_num=10, fields=['title', 'file'], can_delete=True, formset=BaseManageFileFormSet)
Expand Down
8 changes: 4 additions & 4 deletions dojo/importers/base_importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,13 +320,13 @@ def update_test_meta(
fields used today are `version`, `branch_tag`, `build_id`, and `commit_hash`
"""
# Add the extra fields to the test if they are specified here
if not (version := kwargs.get("version", "")).isspace():
if (version := kwargs.get("version", None)) is not None:
test.version = version
if not (branch_tag := kwargs.get("branch_tag", "")).isspace():
if (branch_tag := kwargs.get("branch_tag", None)) is not None:
test.branch_tag = branch_tag
if not (build_id := kwargs.get("build_id", "")).isspace():
if (build_id := kwargs.get("build_id", None)) is not None:
test.build_id = build_id
if not (commit_hash := kwargs.get("commit_hash", "")).isspace():
if (commit_hash := kwargs.get("commit_hash", None)) is not None:
test.commit_hash = commit_hash

return test
Expand Down
5 changes: 3 additions & 2 deletions dojo/importers/default_importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def process_scan(
engagement: Engagement = None,
test: Test = None,
user: Dojo_User = None,
parsed_findings: List[Finding] = None,
parsed_findings: List[Finding] = [],
**kwargs: dict,
) -> Tuple[Test, int, int, int, int, int, Test_Import]:
"""
Expand Down Expand Up @@ -129,7 +129,8 @@ def process_scan(
parser = self.get_parser(scan_type)
# Get the findings from the parser based on what methods the parser supplies
# This could either mean traditional file parsing, or API pull parsing
test, parsed_findings = self.parse_findings(parser, scan_type, scan, test=None, engagement=engagement, **kwargs)
if len(parsed_findings) == 0 or test is None:
test, parsed_findings = self.parse_findings(parser, scan_type, scan, test=test, engagement=engagement, **kwargs)
# process the findings in the foreground or background
new_findings = self.determine_process_method(test, parsed_findings, user, **kwargs)
# Close any old findings in the processed list if the the user specified for that
Expand Down
5 changes: 3 additions & 2 deletions dojo/importers/default_reimporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def process_scan(
engagement: Engagement = None,
test: Test = None,
user: Dojo_User = None,
parsed_findings: List[Finding] = None,
parsed_findings: List[Finding] = [],
**kwargs: dict,
) -> Tuple[Test, int, int, int, int, int, Test_Import]:
"""
Expand Down Expand Up @@ -86,7 +86,8 @@ def process_scan(
parser = self.get_parser(scan_type)
# Get the findings from the parser based on what methods the parser supplies
# This could either mean traditional file parsing, or API pull parsing
parsed_findings = self.parse_findings(parser, scan_type, scan, test=test, engagement=engagement, **kwargs)
if len(parsed_findings) == 0:
parsed_findings = self.parse_findings(parser, scan_type, scan, test=test, engagement=engagement, **kwargs)
# process the findings in the foreground or background
(
new_findings,
Expand Down
6 changes: 5 additions & 1 deletion dojo/templates/dojo/view_eng.html
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ <h4>
<table class="tablesorter-bootstrap table table-condensed table-striped">
<thead>
<tr>
{% block tests_header %}
<th></th>
<th>Title / Type</th>
<th>Date</th>
Expand All @@ -254,11 +255,13 @@ <h4>
{% if 'TRACK_IMPORT_HISTORY'|setting_enabled %}
<th>Reimports</th>
{% endif %}
{% endblock tests_header %}
</tr>
</thead>
<tbody>
{% for test in tests %}
<tr>
{% block test_body %}
<td>
<div class="dropdown">
<a href="#" id="test-menu" class="dropdown-toggle pull-left" data-toggle="dropdown">&nbsp;<i class="fa-solid fa-ellipsis-vertical"></i>&nbsp;</a>
Expand Down Expand Up @@ -349,6 +352,7 @@ <h4>
{{ test.total_reimport_count }}
</td>
{% endif %}
{% endblock test_body %}
</tr>
{% endfor %}
</tbody>
Expand Down Expand Up @@ -691,7 +695,7 @@ <h4>Files<span class="pull-right">
<div class="col-md-2" style="text-align: center">
<div class="row">
{% url 'access_file' fid=file.id oid=eng.id obj_type='Engagement' as image_url %}
<a href="{{ image_url }}" target="_blank">
<a href="{{ image_url }}" target="_blank" download>
{% if file|get_thumbnail %}
<img src="{{ image_url }}" alt="thumbnail" style="width:150px">
{% else %}
Expand Down
2 changes: 1 addition & 1 deletion dojo/templates/dojo/view_finding.html
Original file line number Diff line number Diff line change
Expand Up @@ -943,7 +943,7 @@ <h4>Files<span class="pull-right">
<div class="col-md-2" style="text-align: center">
<div class="row">
{% url 'access_file' fid=file.id oid=finding.id obj_type='Finding' as image_url %}
<a href="{{ image_url }}" target="_blank">
<a href="{{ image_url }}" target="_blank" download>
{% if file|get_thumbnail %}
<img src="{{ image_url }}" alt="thumbnail" style="width:150px">
{% else %}
Expand Down
6 changes: 5 additions & 1 deletion dojo/templates/dojo/view_test.html
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ <h3 class="pull-left">
<div class="table-responsive">
<table class="table-striped table table-condensed table-hover centered">
<tr>
{% block test_header %}
<th>{% trans "Engagement" %}</th>
<th>{% trans "Environment" %}</th>
<th>{% trans "Dates" %}</th>
Expand All @@ -120,8 +121,10 @@ <h3 class="pull-left">
</th>
{% endif %}
{% if test.api_scan_configuration %}<th>{% trans "API Scan Configuration" %}</th>{% endif %}
{% endblock test_header %}
</tr>
<tr>
{% block test_body %}
<td>
<a href="{% url 'view_engagement' test.engagement.id %}">{{ test.engagement.name }}</a>
</td>
Expand Down Expand Up @@ -168,6 +171,7 @@ <h3 class="pull-left">
{% endif %}
</td>
{% endif %}
{% endblock test_body %}
</tr>
</table>
</div>
Expand Down Expand Up @@ -1551,7 +1555,7 @@ <h4>
<div class="col-md-2" style="text-align: center">
<div class="row">
{% url 'access_file' fid=file.id oid=test.id obj_type='Test' as image_url %}
<a href="{{ image_url }}" target="_blank">
<a href="{{ image_url }}" target="_blank" download>
{% if file|get_thumbnail %}
<img src="{{ image_url }}" alt="thumbnail" style="width:150px">
{% else %}
Expand Down
2 changes: 1 addition & 1 deletion dojo/templates/notifications/mail/other.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<br/>
<br/>
{% blocktranslate trimmed with event_url=url|full_url %}
More information on this event can be found here: {{ event_url }}
More information on this event can be found here: <a href="{{ event_url }}">{{ event_url }}</a>
{% endblocktranslate %}
{% endif %}
<br/>
Expand Down
2 changes: 1 addition & 1 deletion dojo/templates/notifications/mail/report_created.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</p>
<p>
{% blocktranslate trimmed with report_name=report.name report_url=url|full_url %}
Your report "{{ report_name }}" is ready. It can be downloaded here: {{ report_url }}
Your report "{{ report_name }}" is ready. It can be downloaded here: <a href="{{ report_url }}">{{ report_url }}</a>
{% endblocktranslate %}
</p>
{% trans "Kind regards" %}, <br/>
Expand Down
2 changes: 1 addition & 1 deletion dojo/templates/notifications/mail/review_requested.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
{{ note }}
<br/>
<br/>
It can be reviewed at {{ url|full_url }}
It can be reviewed at <a href="{{ url|full_url }}">{{ url|full_url }}</a>
</p>
<br/>
{% trans "Kind regards" %}, <br/>
Expand Down
2 changes: 1 addition & 1 deletion dojo/templates/notifications/mail/user_mentioned.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<br/>
{{ note }}<br/>
<br/>
It can be reviewed at {{ url }}
It can be reviewed at <a href="{{ url }}">{{ url }}</a>
{% endblocktranslate %}
</p>

Expand Down
2 changes: 2 additions & 0 deletions dojo/tools/sonarqube/soprasteria_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ def convert_sonar_severity(self, sonar_severity):
return "Medium"
elif sev == "minor":
return "Low"
elif sev in ["high", "medium", "low"]:
return sev.capitalize()
else:
return "Info"

Expand Down
4 changes: 2 additions & 2 deletions helm/defectdojo/Chart.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
apiVersion: v2
appVersion: "2.34.2"
appVersion: "2.34.3"
description: A Helm chart for Kubernetes to install DefectDojo
name: defectdojo
version: 1.6.128
version: 1.6.129
icon: https://www.defectdojo.org/img/favicon.ico
maintainers:
- name: madchap
Expand Down
74 changes: 74 additions & 0 deletions unittests/scans/sonarqube/issue_10150.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{
"date": "Tuesday, May 7, 2024",
"projectName": "my-project-repo",
"applicationName": "my-project-repo",
"branch": "new-sonar-integration",
"inNewCodePeriod": false,
"allBugs": false,
"fixMissingRule": false,
"noSecurityHotspot": false,
"noRulesInReport": false,
"onlyDetectedRules": false,
"vulnerabilityPhrase": "Vulnerability",
"noCoverage": true,
"vulnerabilityPluralPhrase": "Vulnerabilities",
"sonarBaseURL": "https://sonarqube.internal.eu",
"sonarComponent": "my-project-repo",
"rules": {
"objc:S5982": {
"name": "Changing working directories without verifying the success is security-sensitive",
"htmlDesc": "<p>The purpose of changing the current working directory is to modify the base path when the process performs relative path resolutions. When the\nworking directory cannot be changed, the process keeps the directory previously defined as the active working directory. Thus, verifying the success\nof chdir() type of functions is important to prevent unintended relative paths and unauthorized access.</p>\n<h2>Ask Yourself Whether</h2>\n<ul>\n <li> The success of changing the working directory is relevant for the application. </li>\n <li> Changing the working directory is required by chroot to make the new root effective. </li>\n <li> Subsequent disk operations are using relative paths. </li>\n</ul>\n<p>There is a risk if you answered yes to any of those questions.</p>\n<h2>Recommended Secure Coding Practices</h2>\n<p>After changing the current working directory verify the success of the operation and handle errors.</p>\n<h2>Sensitive Code Example</h2>\n<p>The <code>chdir</code> operation could fail and the process still has access to unauthorized resources. The return code should be verified:</p>\n<pre>\nconst char* any_dir = \"/any/\";\nchdir(any_dir); // Sensitive: missing check of the return value\n\nint fd = open(any_dir, O_RDONLY | O_DIRECTORY);\nfchdir(fd); // Sensitive: missing check of the return value\n</pre>\n<h2>Compliant Solution</h2>\n<p>Verify the return code of <code>chdir</code> and handle errors:</p>\n<pre>\nconst char* root_dir = \"/jail/\";\nif (chdir(root_dir) == -1) {\n exit(-1);\n} // Compliant\n\nint fd = open(any_dir, O_RDONLY | O_DIRECTORY);\nif(fchdir(fd) == -1) {\n exit(-1);\n} // Compliant\n</pre>\n<h2>See</h2>\n<ul>\n <li> OWASP - <a href=\"https://owasp.org/Top10/A01_2021-Broken_Access_Control/\">Top 10 2021 Category A1 - Broken Access Control</a> </li>\n <li> OWASP - <a href=\"https://owasp.org/www-project-top-ten/2017/A5_2017-Broken_Access_Control\">Top 10 2017 Category A5 - Broken Access Control</a>\n </li>\n <li> CWE - <a href=\"https://cwe.mitre.org/data/definitions/252\">CWE-252 - Unchecked Return Value</a> </li>\n <li> <a href=\"https://man7.org/linux/man-pages/man2/chdir.2.html\">man7.org</a> - chdir </li>\n</ul>",
"severity": "CRITICAL"
},
"mule4-repository:configuration.13": {
"name": "Domain - Mule Secure Properties should use AES-CBC algorithm",
"htmlDesc": "<b>Domain</b> - Mule Secure Properties should use AES-CBC algorithm",
"severity": "MAJOR"
}
},
"issues": [
{
"rule": "python:S3752",
"severity": "HIGH",
"status": "TO_REVIEW",
"component": "app.py",
"line": 90,
"description": "Allowing both safe and unsafe HTTP methods is security-sensitive",
"message": "Make sure allowing safe and unsafe HTTP methods is safe here.",
"key": "fe0b8add-a857-4136-9a8a-0bdc39ee3204"
},
{
"rule": "python:S4502",
"severity": "HIGH",
"status": "TO_REVIEW",
"component": "app.py",
"line": 27,
"description": "Disabling CSRF protections is security-sensitive",
"message": "Make sure disabling CSRF protection is safe here.",
"key": "d9e751f5-31da-42c0-842e-53f659cec80b"
},
{
"rule": "docker:S6471",
"severity": "MEDIUM",
"status": "TO_REVIEW",
"component": "Dockerfile",
"line": 1,
"description": "Running containers as a privileged user is security-sensitive",
"message": "The python image runs with root as the default user. Make sure it is safe here.",
"key": "dc781f67-3704-47a0-9df1-565d19a2bf23"
}
],
"hotspotKeys": [
"fe0b8add-a857-4136-9a8a-0bdc39ee3204",
"d9e751f5-31da-42c0-842e-53f659cec80b",
"dc781f67-3704-47a0-9df1-565d19a2bf23"
],
"deltaAnalysis": "No",
"qualityGateStatus": false,
"summary": {
"high": 2,
"medium": 1,
"low": 0
}
}

13 changes: 13 additions & 0 deletions unittests/tools/test_sonarqube_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -650,3 +650,16 @@ def test_parse_json_file_from_api_with_multiple_findings_zip(self):
self.assertEqual("typescript:S112533_fjoiewfjo1235gweifjoihugu-", item.title)
self.assertEqual("Medium", item.severity)
my_file_handle.close()

def test_parse_json_file_issue_10150(self):
my_file_handle, _product, _engagement, test = self.init(
get_unit_tests_path() + "/scans/sonarqube/issue_10150.json"
)
parser = SonarQubeParser()
findings = parser.get_findings(my_file_handle, test)
self.assertEqual(3, len(findings))
item = findings[0]
self.assertEqual("High", item.severity)
item = findings[2]
self.assertEqual("Medium", item.severity)
my_file_handle.close()

0 comments on commit 2234848

Please sign in to comment.