diff --git a/combo/apps/wcs/views.py b/combo/apps/wcs/views.py index 3c133c38..e2530d5b 100644 --- a/combo/apps/wcs/views.py +++ b/combo/apps/wcs/views.py @@ -18,7 +18,7 @@ import re from django.contrib import messages from django.conf import settings -from django.core.exceptions import PermissionDenied +from django.core.exceptions import DisallowedRedirect, PermissionDenied from django.core.urlresolvers import reverse from django.http import JsonResponse, HttpResponseRedirect, HttpResponseBadRequest from django.utils.http import urlquote @@ -33,6 +33,7 @@ from .models import TrackingCodeInputCell from .utils import get_wcs_services from combo.utils import requests +from combo.utils.misc import is_url_from_known_service class TrackingCodeView(View): @@ -86,6 +87,9 @@ class TrackingCodeView(View): redirect_to_other_domain = bool( next_netloc and next_netloc != urlparse.urlparse(request.build_absolute_uri()).netloc) + if redirect_to_other_domain and not is_url_from_known_service(next_url): + raise DisallowedRedirect('Unsafe redirect to unknown host') + try: url = self.search(code, request, wcs_site=cell.wcs_site) except PermissionDenied: diff --git a/combo/utils/misc.py b/combo/utils/misc.py index d64273e6..e91ca9f7 100644 --- a/combo/utils/misc.py +++ b/combo/utils/misc.py @@ -15,7 +15,9 @@ # along with this program. If not, see . from django.utils.six.moves.html_parser import HTMLParser +from django.utils.six.moves.urllib import parse as urlparse +from django.conf import settings from django.template.context import BaseContext from django.utils.html import strip_tags @@ -37,3 +39,15 @@ def flatten_context(context): else: flat_context.update(context) return flat_context + + +def is_url_from_known_service(url): + netloc = urlparse.urlparse(url).netloc + if not netloc: + return True + for service_id in settings.KNOWN_SERVICES or {}: + for service_key in settings.KNOWN_SERVICES[service_id]: + service = settings.KNOWN_SERVICES[service_id][service_key] + if urlparse.urlparse(service.get('url')).netloc == netloc: + return True + return False diff --git a/tests/test_wcs.py b/tests/test_wcs.py index c539e632..7d184b8b 100644 --- a/tests/test_wcs.py +++ b/tests/test_wcs.py @@ -756,16 +756,22 @@ def test_tracking_code_cell(app, nocache): # simulate cell being displayed on a different site resp = app.get('/') - resp.form['url'] = 'http://example.net/' + resp.form['url'] = 'http://example.org/' resp.form['code'] = 'CNPHNTFB' resp = resp.form.submit() - assert resp.location == 'http://example.net/?unknown-tracking-code' + assert resp.location == 'http://example.org/?unknown-tracking-code' resp = app.get('/') - resp.form['url'] = 'http://example.net/?foo=bar' + resp.form['url'] = 'http://example.org/?foo=bar' resp.form['code'] = 'CNPHNTFB' resp = resp.form.submit() - assert resp.location == 'http://example.net/?foo=bar&unknown-tracking-code' + assert resp.location == 'http://example.org/?foo=bar&unknown-tracking-code' + + # redirect to an unknown site + resp = app.get('/') + resp.form['url'] = 'http://example.net/' + resp.form['code'] = 'CNPHNTFB' + resp = resp.form.submit(status=400) # error handling resp = app.get('/')