From 38d972b9ecb01fdb863c686de0b958389bc20220 Mon Sep 17 00:00:00 2001 From: Gary Wilson Jr Date: Sun, 18 Nov 2007 06:51:20 +0000 Subject: [PATCH] Fixed #5880 -- Fixed an XSS hole in the admin interface. * Escaped text that gets sent after saving the admin foreignkey popup form. * Added quotes around the second argument passed to `opener.dismissAddAnotherPopup` to make the function also work when a text field is used as the primary key. * Added a `html_unescape` javascript function to unescape the strings passed in to the `dismissAddAnotherPopup` function so that the added choice displays correctly in the dropdown box. git-svn-id: http://code.djangoproject.com/svn/django/trunk@6691 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- .../admin/media/js/admin/RelatedObjectLookups.js | 14 ++++++++++++++ django/contrib/admin/views/main.py | 7 +++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/django/contrib/admin/media/js/admin/RelatedObjectLookups.js b/django/contrib/admin/media/js/admin/RelatedObjectLookups.js index 36ae21411d..f6a39ca091 100644 --- a/django/contrib/admin/media/js/admin/RelatedObjectLookups.js +++ b/django/contrib/admin/media/js/admin/RelatedObjectLookups.js @@ -1,6 +1,16 @@ // Handles related-objects functionality: lookup link for raw_id_admin=True // and Add Another links. +function html_unescape(text) { + // Unescape a string that was escaped using django.utils.html.escape. + text = text.replace(/</g, '<'); + text = text.replace(/>/g, '>'); + text = text.replace(/"/g, '"'); + text = text.replace(/'/g, "'"); + text = text.replace(/&/g, '&'); + return text; +} + function showRelatedObjectLookupPopup(triggeringLink) { var name = triggeringLink.id.replace(/^lookup_/, ''); // IE doesn't like periods in the window name, so convert temporarily. @@ -42,6 +52,10 @@ function showAddAnotherPopup(triggeringLink) { } function dismissAddAnotherPopup(win, newId, newRepr) { + // newId and newRepr are expected to have previously been escaped by + // django.utils.html.escape. + newId = html_unescape(newId); + newRepr = html_unescape(newRepr); var name = win.name.replace(/___/g, '.'); var elem = document.getElementById(name); if (elem) { diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py index 947d09b852..9786935bf8 100644 --- a/django/contrib/admin/views/main.py +++ b/django/contrib/admin/views/main.py @@ -273,10 +273,9 @@ def add_stage(request, app_label, model_name, show_delete=False, form_url='', po post_url_continue += "?_popup=1" return HttpResponseRedirect(post_url_continue % pk_value) if "_popup" in request.POST: - if type(pk_value) is str: # Quote if string, so JavaScript doesn't think it's a variable. - pk_value = '"%s"' % pk_value.replace('"', '\\"') - return HttpResponse('' % \ - (pk_value, force_unicode(new_object).replace('"', '\\"'))) + return HttpResponse('' % \ + # escape() calls force_unicode. + (escape(pk_value), escape(new_object))) elif "_addanother" in request.POST: request.user.message_set.create(message=msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name))) return HttpResponseRedirect(request.path)