Skip to content

Commit ba0ac3f

Browse files
committed
fix: Make some security measures explicit
1 parent b7f7160 commit ba0ac3f

File tree

5 files changed

+30
-28
lines changed

5 files changed

+30
-28
lines changed

cms/admin/pageadmin.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
HttpResponseBadRequest,
2727
HttpResponseForbidden,
2828
HttpResponseRedirect,
29+
JsonResponse,
2930
QueryDict,
3031
)
3132
from django.shortcuts import get_object_or_404, render
@@ -343,7 +344,7 @@ def set_home(self, request, object_id):
343344
raise self._get_404_exception(object_id)
344345

345346
if not page.is_potential_home():
346-
return HttpResponseBadRequest(_("The page is not eligible to be home."))
347+
return HttpResponseBadRequest(escape(_("The page is not eligible to be home.")))
347348

348349
new_home_tree, old_home_tree = page.set_as_homepage(request.user)
349350

@@ -389,7 +390,7 @@ def get_list(self, *args, **kwargs):
389390
"redirect_url": page.get_absolute_url(language=language_code),
390391
}
391392
)
392-
return HttpResponse(json.dumps(results), content_type="application/json")
393+
return JsonResponse(results, safe=False)
393394
return HttpResponseForbidden()
394395

395396
def changelist_view(self, request, extra_context=None):
@@ -555,15 +556,15 @@ def move_page(self, request, page_id, extra_context=None):
555556
form = self.move_form(request.POST or None, page=page, site=site)
556557

557558
if not form.is_valid():
558-
return jsonify_request(HttpResponseBadRequest(str(form.errors.get("__all__", _("error")))))
559+
return jsonify_request(HttpResponseBadRequest(escape(form.errors.get("__all__", _("error")))))
559560

560561
target = form.cleaned_data["target"]
561562
can_move_page = self.has_move_page_permission(request, obj=page)
562563

563564
# Does the user have permissions to do this...?
564565
if not can_move_page or (target and not target.has_add_permission(user)):
565566
message = _("Error! You don't have permissions to move this page. Please reload the page")
566-
return jsonify_request(HttpResponseForbidden(message))
567+
return jsonify_request(HttpResponseForbidden(escape(message)))
567568

568569
operation_token = send_pre_page_operation(
569570
request=request,
@@ -679,14 +680,14 @@ def copy_page(self, request, page_id):
679680
return jsonify_request(HttpResponseBadRequest(message))
680681

681682
new_page = form.copy_page(user)
682-
return HttpResponse(json.dumps({"id": new_page.pk}), content_type="application/json")
683+
return JsonResponse({"id": new_page.pk})
683684

684685
def edit_title_fields(self, request, page_id, language):
685686
page = self.get_object(request, object_id=page_id)
686687
translation = page.get_admin_content(language)
687688

688689
if not self.has_change_permission(request, obj=page):
689-
return HttpResponseForbidden(_("You do not have permission to edit this page"))
690+
return HttpResponseForbidden(escape(_("You do not have permission to edit this page")))
690691

691692
if page is None:
692693
raise self._get_404_exception(page_id)
@@ -1180,15 +1181,15 @@ def change_template(self, request, object_id):
11801181

11811182
if get_cms_setting("TEMPLATES"):
11821183
if to_template not in dict(get_cms_setting("TEMPLATES")):
1183-
return HttpResponseBadRequest(_("Template not valid"))
1184+
return HttpResponseBadRequest(escape(_("Template not valid")))
11841185
else:
11851186
if to_template not in (placeholder_set[0] for placeholder_set in get_cms_setting("PLACEHOLDERS")):
1186-
return HttpResponseBadRequest(_("Placeholder selection not valid"))
1187+
return HttpResponseBadRequest(escape(_("Placeholder selection not valid")))
11871188

11881189
page_content.template = to_template
11891190
page_content.save()
11901191

1191-
return HttpResponse(_("The template was successfully changed"))
1192+
return HttpResponse(escape(_("The template was successfully changed")))
11921193

11931194
@require_POST
11941195
@transaction.atomic
@@ -1205,7 +1206,7 @@ def copy_language(self, request, object_id):
12051206
page = source_page_content.page
12061207

12071208
if not target_language or target_language not in get_language_list(site_id=page.site_id):
1208-
return HttpResponseBadRequest(_("Language must be set to a supported language!"))
1209+
return HttpResponseBadRequest(escape(_("Language must be set to a supported language!")))
12091210

12101211
target_page_content = page.get_content_obj(target_language, fallback=False)
12111212

@@ -1218,7 +1219,7 @@ def copy_language(self, request, object_id):
12181219
plugins = placeholder.get_plugins_list(source_page_content.language)
12191220

12201221
if not target.has_add_plugins_permission(request.user, plugins):
1221-
return HttpResponseForbidden(_("You do not have permission to copy these plugins."))
1222+
return HttpResponseForbidden(escape(_("You do not have permission to copy these plugins.")))
12221223
copy_plugins_to_placeholder(plugins, target, language=target_language)
12231224
return HttpResponse("ok")
12241225

@@ -1227,7 +1228,7 @@ def delete_view(self, request, object_id, extra_context=None):
12271228
page = page_content.page
12281229

12291230
if not self.has_delete_translation_permission(request, page_content.language, page):
1230-
return HttpResponseForbidden(_("You do not have permission to delete this page"))
1231+
return HttpResponseForbidden(escape(_("You do not have permission to delete this page")))
12311232

12321233
if page is None:
12331234
raise self._get_404_exception(object_id)
@@ -1291,7 +1292,7 @@ def change_innavigation(self, request, object_id):
12911292
else:
12921293
# Only this page? Can be permissions or versioning, or ...
12931294
message = "You cannot change this page's navigation status"
1294-
return HttpResponseForbidden(_(message))
1295+
return HttpResponseForbidden(escape(_(message)))
12951296

12961297
if page_content is None:
12971298
raise self._get_404_exception(object_id)

cms/admin/placeholderadmin.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
HttpResponseBadRequest,
1515
HttpResponseForbidden,
1616
HttpResponseNotFound,
17+
JsonResponse,
1718
)
1819
from django.shortcuts import get_list_or_404, get_object_or_404
20+
from django.template.defaultfilters import escape
1921
from django.template.response import TemplateResponse
2022
from django.urls import include, re_path
2123
from django.utils.datastructures import MultiValueDict
@@ -386,12 +388,11 @@ def add_plugin(self, request):
386388

387389
if not self.has_add_plugin_permission(request, placeholder, plugin_type):
388390
message = _('You do not have permission to add a plugin')
389-
return HttpResponseForbidden(message)
391+
return HttpResponseForbidden(escape(message))
390392

391393
if not placeholder.check_source(request.user):
392394
message = _('You do not have permission to add a plugin')
393-
return HttpResponseForbidden(message)
394-
395+
return HttpResponseForbidden(escape(message))
395396
plugin_class = plugin_pool.get_plugin(plugin_type)
396397
plugin_instance = plugin_class(plugin_class.model, self.admin_site)
397398

@@ -464,7 +465,7 @@ def copy_plugins(self, request):
464465
target_placeholder = get_object_or_404(Placeholder, pk=target_placeholder_id)
465466

466467
if not target_language or target_language not in get_language_list():
467-
return HttpResponseBadRequest(_("Language must be set to a supported language!"))
468+
return HttpResponseBadRequest(escape(_("Language must be set to a supported language!")))
468469

469470
copy_to_clipboard = target_placeholder.pk == request.toolbar.clipboard.pk
470471
source_plugin_id = request.POST.get('source_plugin_id', None)
@@ -489,7 +490,7 @@ def copy_plugins(self, request):
489490
target_placeholder,
490491
)
491492
data = get_plugin_tree(request, new_plugins)
492-
return HttpResponse(json.dumps(data), content_type='application/json')
493+
return JsonResponse(data)
493494

494495
def _copy_plugin_to_clipboard(self, request, target_placeholder):
495496
source_language = request.POST['source_language']
@@ -624,15 +625,15 @@ def edit_plugin(self, request, plugin_id):
624625
try:
625626
plugin_id = int(plugin_id)
626627
except ValueError:
627-
return HttpResponseNotFound(_("Plugin not found"))
628+
return HttpResponseNotFound(escape(_("Plugin not found")))
628629

629630
obj = self._get_plugin_from_id(plugin_id)
630631

631632
# CMSPluginBase subclass instance
632633
plugin_instance = obj.get_plugin_class_instance(admin=self.admin_site)
633634

634635
if not self.has_change_plugin_permission(request, obj):
635-
return HttpResponseForbidden(_("You do not have permission to edit this plugin"))
636+
return HttpResponseForbidden(escape(_("You do not have permission to edit this plugin")))
636637

637638
if not obj.placeholder.check_source(request.user):
638639
message = _("You do not have permission to edit this plugin")
@@ -1115,11 +1116,11 @@ def clear_placeholder(self, request, placeholder_id):
11151116

11161117
if not self.has_clear_placeholder_permission(request, placeholder, language):
11171118
message = _("You do not have permission to clear this placeholder")
1118-
return HttpResponseForbidden(message)
1119+
return HttpResponseForbidden(escape(message))
11191120

11201121
if not placeholder.check_source(request.user):
11211122
message = _("You do not have permission to clear this placeholder")
1122-
raise PermissionDenied(message)
1123+
raise PermissionDenied(escape(message))
11231124

11241125
opts = Placeholder._meta
11251126
plugins = placeholder.get_plugins_list(language)
@@ -1136,7 +1137,7 @@ def clear_placeholder(self, request, placeholder_id):
11361137
# The user has already confirmed the deletion.
11371138
if perms_needed:
11381139
message = _("You do not have permission to clear this placeholder")
1139-
return HttpResponseForbidden(message)
1140+
return HttpResponseForbidden(escape(message))
11401141

11411142
operation_token = self._send_pre_placeholder_operation(
11421143
request,

cms/admin/settingsadmin.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
HttpResponse,
1010
HttpResponseBadRequest,
1111
HttpResponseRedirect,
12+
JsonResponse,
1213
)
1314
from django.http.request import QueryDict
1415
from django.urls import Resolver404, path, re_path, resolve
@@ -59,8 +60,7 @@ def session_store(self, request):
5960
POST should have a settings parameter
6061
"""
6162
if not request.user.is_staff:
62-
return HttpResponse(json.dumps(""),
63-
content_type="application/json")
63+
return JsonResponse("", status=403)
6464
if request.method == "POST":
6565
request.session['cms_settings'] = conditional_escape(request.POST['settings'])
6666
request.session.save()

cms/management/commands/startcmsproject.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ def install_requirements(self, project):
115115
self.write_command(f'python -m pip install -r "{requirements}"')
116116
result = subprocess.run(
117117
[sys.executable, "-m", "pip", "install", "-r", requirements],
118-
capture_output=True, check=False,
118+
capture_output=True, check=False, shell=False,
119119
)
120120
if result.returncode:
121121
self.stderr.write(self.style.ERROR(result.stderr.decode()))
@@ -132,7 +132,7 @@ def install_requirements(self, project):
132132

133133
def run_management_command(self, commands, capture_output=False):
134134
self.write_command("python -m manage " + " ".join(commands))
135-
result = subprocess.run([sys.executable, "-m", "manage"] + commands, capture_output=capture_output, check=False)
135+
result = subprocess.run([sys.executable, "-m", "manage"] + commands, capture_output=capture_output, check=False, shell=False)
136136
if result.returncode:
137137
if capture_output:
138138
self.stderr.write(self.style.ERROR(result.stderr.decode()))
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<div class="cms-plugin cms-plugin-{{ plugin.id|safe }}">{{ inner }}</div>
1+
{% load l10n %}<div class="cms-plugin cms-plugin-{{ plugin.id|unlocalize }}">{{ inner }}</div>

0 commit comments

Comments
 (0)