diff --git a/accounts/views.py b/accounts/views.py index 02be62b7..86ae8ebc 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -20,6 +20,7 @@ from django.utils.http import url_has_allowed_host_and_scheme from django.utils.translation import gettext_lazy as _ from django.views.decorators.cache import never_cache from django.views.decorators.http import require_POST +from django.views.decorators.debug import sensitive_post_parameters from accounts.apps import admin_password_path from computes.utils import lock_ownership @@ -186,6 +187,7 @@ def account(request, user_id): ) +@sensitive_post_parameters() @login_required def change_password(request): must_change = UserAttributes.objects.filter( diff --git a/admin/views.py b/admin/views.py index c32a49b8..a9bb11cb 100644 --- a/admin/views.py +++ b/admin/views.py @@ -15,6 +15,7 @@ from django.utils import timezone from django.utils.http import url_has_allowed_host_and_scheme, urlencode from django.utils.translation import gettext_lazy as _ from django.views.decorators.http import require_POST +from django.views.decorators.debug import sensitive_post_parameters from instances.models import InstanceTombstone from instances.report import workbook from logs.audit import audit, changed_fields, snapshot @@ -124,6 +125,7 @@ def user_list(request): ) +@sensitive_post_parameters() @superuser_only def user_create(request): user_form = forms.UserCreateForm(request.POST or None) @@ -149,6 +151,7 @@ def user_create(request): ) +@sensitive_post_parameters() @superuser_only def user_update(request, pk): user = get_object_or_404(User, pk=pk) @@ -190,6 +193,7 @@ def user_update(request, pk): ) +@sensitive_post_parameters() @superuser_only def user_update_password(request, pk): user = get_object_or_404(User, pk=pk) diff --git a/computes/views.py b/computes/views.py index 69d759c3..99b15830 100644 --- a/computes/views.py +++ b/computes/views.py @@ -8,6 +8,7 @@ from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse from django.utils import timezone from django.utils.translation import gettext_lazy as _ +from django.views.decorators.debug import sensitive_post_parameters from instances.models import Instance from instances.utils import read_list_rows from libvirt import libvirtError @@ -145,6 +146,7 @@ def instances(request, compute_id): ) +@sensitive_post_parameters() @superuser_only def compute_create(request, FormClass): form = FormClass(request.POST or None) @@ -157,6 +159,7 @@ def compute_create(request, FormClass): return render(request, "computes/form.html", {"form": form}) +@sensitive_post_parameters() @superuser_only def compute_update(request, compute_id): compute = get_object_or_404(Compute, pk=compute_id) diff --git a/instances/views.py b/instances/views.py index a169a37f..da159006 100755 --- a/instances/views.py +++ b/instances/views.py @@ -38,6 +38,7 @@ from django.utils import timezone from django.utils.translation import gettext as _ from django.utils.translation import gettext_lazy, gettext_noop from django.views.decorators.http import require_POST +from django.views.decorators.debug import sensitive_post_parameters from libvirt import (VIR_DOMAIN_PAUSED, VIR_DOMAIN_RUNNING, VIR_DOMAIN_SHUTOFF, @@ -1091,6 +1092,7 @@ def access_error(request, err): return text +@sensitive_post_parameters() @require_POST @serialize_instance_mutation def set_root_pass(request, pk): @@ -2320,6 +2322,7 @@ def clone(request, pk): return redirect(reverse("instances:instance", args=[pk])) +@sensitive_post_parameters() @require_POST @serialize_instance_mutation def update_console(request, pk): diff --git a/virtsecrets/views.py b/virtsecrets/views.py index 97b2a91d..576dfd80 100644 --- a/virtsecrets/views.py +++ b/virtsecrets/views.py @@ -3,6 +3,7 @@ from computes.models import Compute from django.contrib import messages from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404, render +from django.views.decorators.debug import sensitive_post_parameters from instances.models import Instance from libvirt import libvirtError from logs.audit import audit @@ -11,6 +12,7 @@ from vrtManager.virtsecrets import wvmSecrets from virtsecrets.forms import USAGE_TEXTS, AddSecret +@sensitive_post_parameters() @superuser_only def secrets(request, compute_id): """The host's secrets with the VMs and pools that use them; one created, diff --git a/webvirtcloud/tests/test_sensitive_posts.py b/webvirtcloud/tests/test_sensitive_posts.py new file mode 100644 index 00000000..1b797f07 --- /dev/null +++ b/webvirtcloud/tests/test_sensitive_posts.py @@ -0,0 +1,30 @@ +"""The views that take a password or a key mark their POST data sensitive: +an error report (mail_admins) leaves it out.""" + +from unittest.mock import patch + +from django.contrib.auth import get_user_model +from django.test import TestCase +from django.urls import reverse + +from computes.models import Compute +from instances.models import Instance + + +class SensitivePostTestCase(TestCase): + def test_each_view_that_takes_a_secret_hides_its_post_data(self): + admin = get_user_model().objects.create_superuser("sens-admin", "s@example.com", "pw") + compute = Compute.objects.create(name="sens-c", hostname="192.0.2.90", login="root", password="", type=1) + vm = Instance.objects.create(compute=compute, name="sens-vm", uuid="aaaaaaaa-0000-0000-0000-000000000091") + self.client.force_login(admin) + self.client.raise_request_exception = False + urls = [reverse("admin:user_create"), reverse("admin:user_update", args=[admin.pk]), + reverse("admin:user_update_password", args=[admin.pk]), reverse("accounts:change_password"), + reverse("add_tcp_host"), reverse("compute_update", args=[compute.pk]), + reverse("virtsecrets", args=[compute.pk]), + reverse("instances:rootpasswd", args=[vm.pk]), reverse("instances:update_console", args=[vm.pk])] + with patch("instances.models.wvmInstance"), patch("virtsecrets.views.wvmSecrets"): + for url in urls: + with self.subTest(url): + response = self.client.post(url, {"password": "S3cret"}) + self.assertEqual(response.wsgi_request.sensitive_post_parameters, "__ALL__")