Repository: mdn/kuma

Issue:
wiki.views.translate with `tolocale` invalid causes ISE
E.g. https://sentry.prod.mozaws.net/operations/mdn-prod/issues/6147484/?environment=oregon%3Aprod


```
KeyError: u'he?async'
  File "django/core/handlers/exception.py", line 41, in inner
    response = get_response(request)
  File "django/core/handlers/base.py", line 187, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "django/core/handlers/base.py", line 185, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "newrelic/hooks/framework_django.py", line 544, in wrapper
    return wrapped(*args, **kwargs)
  File "kuma/core/decorators.py", line 213, in wrapped
    return func(request, *args, **kwargs)
  File "django/views/decorators/cache.py", line 57, in _wrapped_view_func
    response = view_func(request, *args, **kwargs)
  File "kuma/core/decorators.py", line 148, in agent_blocked_view
    return view_func(request, *args, **kwargs)
  File "kuma/core/decorators.py", line 78, in _wrapped_view
    return view_fn(request, *args, **kwargs)
  File "csp/decorators.py", line 19, in _wrapped
    r = f(*a, **kw)
  File "kuma/wiki/decorators.py", line 105, in process
    return func(request, *args, **kwargs)
  File "kuma/wiki/decorators.py", line 48, in _check_readonly
    return view(request, *args, **kwargs)
  File "kuma/wiki/decorators.py", line 21, in _added_header
    response = func(request, *args, **kwargs)
  File "kuma/wiki/views/translate.py", line 265, in translate
    language = language_mapping[document_locale.lower()]
```


Discussion:
Easy to reproduce. Just go to a URL like: http://wiki.mdn.localhost:8000/en-US/docs/Web/API/Fullscreen_API$translate?tolocale=plXXXX

Resolving patch:
diff --git a/kuma/wiki/views/translate.py b/kuma/wiki/views/translate.py
--- a/kuma/wiki/views/translate.py
+++ b/kuma/wiki/views/translate.py
@@ -2,7 +2,7 @@
 from csp.decorators import csp_update
 from django.conf import settings
 from django.core.exceptions import ObjectDoesNotExist
-from django.http import JsonResponse
+from django.http import Http404, JsonResponse
 from django.shortcuts import get_object_or_404, redirect, render
 from django.utils.safestring import mark_safe
 from django.utils.six.moves.urllib.parse import urlencode
@@ -62,10 +62,18 @@ def translate(request, document_slug, document_locale):
                                    locale=settings.WIKI_DEFAULT_LANGUAGE,
                                    slug=document_slug)
 
+    # Get the mapping here and now so it can be used for input validation
+    language_mapping = get_language_mapping()
+
     # HACK: Seems weird, but sticking the translate-to locale in a query
     # param is the best way to avoid the MindTouch-legacy locale
     # redirection logic.
     document_locale = request.GET.get('tolocale', document_locale)
+    if document_locale.lower() not in language_mapping:
+        # The 'tolocale' query string parameters aren't free-text. They're
+        # explicitly listed on the "Select language" page (`...$locales`)
+        # If a locale was entered that wasn't a link it's a user bug.
+        raise Http404
 
     # Set a "Discard Changes" page
     discard_href = ''
@@ -261,7 +269,6 @@ def translate(request, document_slug, document_locale):
 
     parent_split = split_slug(parent_doc.slug)
 
-    language_mapping = get_language_mapping()
     language = language_mapping[document_locale.lower()]
     default_locale = language_mapping[settings.WIKI_DEFAULT_LANGUAGE.lower()]
 


Test patch:
diff --git a/kuma/wiki/tests/test_views_translate.py b/kuma/wiki/tests/test_views_translate.py
--- a/kuma/wiki/tests/test_views_translate.py
+++ b/kuma/wiki/tests/test_views_translate.py
@@ -23,7 +23,7 @@ def test_translate_get(root_doc, trans_doc_client):
     """Test GET on the translate view."""
 
     url = reverse('wiki.translate', args=(root_doc.slug,))
-    url += '?tolocale=fr'
+    url += '?tolocale=Fr'
 
     response = trans_doc_client.get(url)
     assert response.status_code == 200
@@ -33,6 +33,17 @@ def test_translate_get(root_doc, trans_doc_client):
     assert page.find('input[name=slug]')[0].value == root_doc.slug
 
 
+def test_translate_get_invalid_locale(root_doc, trans_doc_client):
+    """Test GET on the translate view but with an invalid 'tolocale'
+    query string parameter."""
+
+    url = reverse('wiki.translate', args=(root_doc.slug,))
+    url += '?tolocale=XxX'
+
+    response = trans_doc_client.get(url)
+    assert response.status_code == 404
+
+
 def test_translate_post(root_doc, trans_doc_client):
     """Test POST on the translate view."""