VYPR
Medium severity5.5NVD Advisory· Published Feb 8, 2016· Updated May 6, 2026

CVE-2016-2048

CVE-2016-2048

Description

Django 1.9.x before 1.9.2, when ModelAdmin.save_as is set to True, allows remote authenticated users to bypass intended access restrictions and create ModelAdmin objects via the "Save as New" option when editing objects and leveraging the "change" permission.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
DjangoPyPI
>= 1.9, < 1.9.21.9.2

Patches

1
adbca5e4db42

[1.9.x] Fixed incorrect permissions check for admin's "Save as new".

https://github.com/django/djangoMyk WillisJan 24, 2016via ghsa
3 files changed · +46 6
  • django/contrib/admin/options.py+6 4 modified
    @@ -1343,6 +1343,10 @@ def changeform_view(self, request, object_id=None, form_url='', extra_context=No
     
             model = self.model
             opts = model._meta
    +
    +        if request.method == 'POST' and '_saveasnew' in request.POST:
    +            object_id = None
    +
             add = object_id is None
     
             if add:
    @@ -1360,10 +1364,6 @@ def changeform_view(self, request, object_id=None, form_url='', extra_context=No
                     raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {
                         'name': force_text(opts.verbose_name), 'key': escape(object_id)})
     
    -            if request.method == 'POST' and "_saveasnew" in request.POST:
    -                object_id = None
    -                obj = None
    -
             ModelForm = self.get_form(request, obj)
             if request.method == 'POST':
                 form = ModelForm(request.POST, request.FILES, instance=obj)
    @@ -1426,6 +1426,8 @@ def changeform_view(self, request, object_id=None, form_url='', extra_context=No
             if request.method == 'POST' and not form_validated and "_saveasnew" in request.POST:
                 context['show_save'] = False
                 context['show_save_and_continue'] = False
    +            # Use the change template instead of the add template.
    +            add = False
     
             context.update(extra_context or {})
     
    
  • docs/releases/1.9.2.txt+11 2 modified
    @@ -4,8 +4,17 @@ Django 1.9.2 release notes
     
     *Under development*
     
    -Django 1.9.2 fixes several bugs in 1.9.1 and makes a small backwards
    -incompatible change that hopefully doesn't affect any users.
    +Django 1.9.2 fixes a security regression in 1.9 and several bugs in 1.9.1. It
    +also makes a small backwards incompatible change that hopefully doesn't affect
    +any users.
    +
    +Security issue: User with "change" but not "add" permission can create objects for ``ModelAdmin``’s with ``save_as=True``
    +=========================================================================================================================
    +
    +If a ``ModelAdmin`` uses ``save_as=True`` (not the default), the admin
    +provides an option when editing objects to "Save as new". A regression in
    +Django 1.9 prevented that form submission from raising a "Permission Denied"
    +error for users without the "add" permission.
     
     Backwards incompatible change: ``.py-tpl`` files rewritten in project/app templates
     ===================================================================================
    
  • tests/admin_views/tests.py+29 0 modified
    @@ -1712,6 +1712,35 @@ def test_change_view(self):
                 self.assertContains(response, 'login-form')
                 self.client.get(reverse('admin:logout'))
     
    +    def test_change_view_save_as_new(self):
    +        """
    +        'Save as new' should raise PermissionDenied for users without the 'add'
    +        permission.
    +        """
    +        change_dict_save_as_new = {
    +            '_saveasnew': 'Save as new',
    +            'title': 'Ikke fordømt',
    +            'content': '<p>edited article</p>',
    +            'date_0': '2008-03-18', 'date_1': '10:54:39',
    +            'section': self.s1.pk,
    +        }
    +        article_change_url = reverse('admin:admin_views_article_change', args=(self.a1.pk,))
    +
    +        # Add user can perform "Save as new".
    +        article_count = Article.objects.count()
    +        self.client.force_login(self.adduser)
    +        post = self.client.post(article_change_url, change_dict_save_as_new)
    +        self.assertRedirects(post, self.index_url)
    +        self.assertEqual(Article.objects.count(), article_count + 1)
    +        self.client.logout()
    +
    +        # Change user cannot perform "Save as new" (no 'add' permission).
    +        article_count = Article.objects.count()
    +        self.client.force_login(self.changeuser)
    +        post = self.client.post(article_change_url, change_dict_save_as_new)
    +        self.assertEqual(post.status_code, 403)
    +        self.assertEqual(Article.objects.count(), article_count)
    +
         def test_delete_view(self):
             """Delete view should restrict access and actually delete items."""
             delete_dict = {'post': 'yes'}
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

10

News mentions

0

No linked articles in our index yet.