Repository: mdn/kuma

Issue:
Strict MIME checking broke the demos style
# Request type
<!-- Select the appropriate option -->
- [ ] Please close this issue, I accidentally submitted it without adding any details
- [ ] New documentation
- [x] Correction or update


# Details
<!-- Tell us about the issue you saw. A clear description, links, and screenshots help us fix it faster. -->
Error loading https://developer.mozilla.org/en-US/docs/Web/CSS/resize et al.:

"Refused to apply style from 'https://mdn.mozillademos.org/static/build/styles/samples.37902ba3b7fe.css' because its MIME type ('text/html') is not a supported stylesheet MIME type, and strict MIME checking is enabled."




Discussion:
@schalkneethling looks like strict MIME type checking is stopping the application of global page styles to the live example IFrames. Do we need this reporting somewhere else?
(Confirmed in both Chrome and Fx)
> @schalkneethling looks like strict MIME type checking is stopping the application of global page styles to the live example IFrames. Do we need this reporting somewhere else?

Good question :) Perhaps look it over on Kuma and we can take it from there?
@schalkneethling I've refiled this at https://trello.com/c/jtyroCSH/15-strict-mime-checking-broke-the-demos-style. Can you make this one a priority?
> @schalkneethling I've refiled this at https://trello.com/c/jtyroCSH/15-strict-mime-checking-broke-the-demos-style. Can you make this one a priority?

@atopal @tobinmori ?
@chrisdavidmills can you provide a bit more context? What's broken on the page because of this? And is this specific to the page you mentioned or have you seen this on other pages too?
This si the only page I've seen that does this, but I am worried that more live ssamples will be affected. It is indicating that some styling is not being applied to the demo inside the IFrame because of this, so it should at least be investigated.
Marking as a P2 for the next sprint
Generally the problem isn't with MIME types. [The iframe](https://mdn.mozillademos.org/en-US/docs/Web/CSS/resize$samples/Using_resize_with_arbitrary_elements?revision=1528269) uses 
```
<link href="/static/build/styles/samples.37902ba3b7fe.css" rel="stylesheet" type="text/css" />
```
which means that the browser is GET'ing `https://mdn.mozillademos.org/static/build/styles/samples.37902ba3b7fe.css` but that URL causes a 404. And it also contains this HTTP response header:
```
x-cache: Error from cloudfront
```
For the record, `https://developer.mozilla.org/static/build/styles/samples.37902ba3b7fe.css` works and I've double-checked that that file really does exist inside the docker container in production. 
I'm understanding it a bit more now. This is a DevOps problem. Not a "traditional" web dev problem. It's about how we do AWS and stuff in Kuma. 

First of all, the `x-cache: Error from cloudfront` means when you use CloudFront and the origin yields a 404. For example:
```
▶ curl -I https://developer.mozilla.org/static/build/styles/home.67907b377f79.css
HTTP/2 200
...
x-cache: Hit from cloudfront
...

▶ curl -I https://developer.mozilla.org/static/build/styles/home.doesnotexist.css
HTTP/2 404
...
x-cache: Error from cloudfront
...
```

What happens is not that our Django code fails to find the file. The file `/static/build/styles/samples.37902ba3b7fe.css` totally exists on disk. 
However, the way we use Whitenoise is such that if the host you use is not `settings.ATTACHMENT_ORIGIN` or `settings.ATTACHMENT_HOST` our Whitenoise override  basically says to Whitenoise to not bother and Whitenoise thinks this means the file doesn't exist. 

The lines responsible for this is here: https://github.com/mdn/kuma/blob/5a9fb57aa304ed0724e7fd1cff89b4540ba0f0de/kuma/core/middleware.py#L343-L344

I think that `return None` should be replaced with `return HttpResponseForbidden()` to avoid this kind of misleading debugging. I'll make a PR for that. 

Now let's talk about domains, origins, and attachments...
When `GET` a URL like `/static/build/styles/samples.37902ba3b7fe.css` in an iframe, on a page like https://developer.mozilla.org/en-US/docs/Web/CSS/resize it ultimately means that the iframe runs within `mdn.mozillademos.org`. So the URL we're struggling with is https://mdn.mozillademos.org/static/build/styles/samples.37902ba3b7fe.css

In production, `mdn.mozillademos.org` is actually the value of `settings.ATTACHMENT_HOST` which according to `RestrictedWhiteNoiseMiddleware` is "untrusted". 

In other words, we're not allowing Whitenoise to serve any static files unless the host is `developer.mozilla.org` (our main CloudFront domain) or `prod.mdn.mozit.cloud` (our CloudFront origin domain). 
So, we're happy to serve things like `/en-US/docs/Web/CSS/resize$samples/...` under `mdn.mozillademos.org` but not things like `/static/...`. 

I can think of hacks to do to remedy this now, but nothing that I'm comfortable with in a quick fix. The reasons for these restrictions and stuff is related to security which is a minefield in the case of interactive examples. 

The problem is the same with [`wiki.developer.mozilla.org` too](https://wiki.developer.mozilla.org/en-US/docs/Web/CSS/resize) by the way. 

I think I'd rather leverage @escattone 's opinions on this since he's most likely more aware of the implications.





Resolving patch:
diff --git a/kuma/core/middleware.py b/kuma/core/middleware.py
--- a/kuma/core/middleware.py
+++ b/kuma/core/middleware.py
@@ -9,7 +9,6 @@
 from django.urls import get_script_prefix, resolve, Resolver404
 from django.utils.encoding import smart_str
 from waffle.middleware import WaffleMiddleware
-from whitenoise.middleware import WhiteNoiseMiddleware
 
 from kuma.wiki.views.legacy import (mindtouch_to_kuma_redirect,
                                     mindtouch_to_kuma_url)
@@ -336,17 +335,6 @@ def __call__(self, request):
         return self.get_response(request)
 
 
-class RestrictedWhiteNoiseMiddleware(WhiteNoiseMiddleware):
-    """Restricts the use of WhiteNoiseMiddleware based on the host."""
-
-    def process_request(self, request):
-        if settings.ENABLE_RESTRICTIONS_BY_HOST and is_untrusted(request):
-            return None
-        return super(RestrictedWhiteNoiseMiddleware, self).process_request(
-            request
-        )
-
-
 class WaffleWithCookieDomainMiddleware(WaffleMiddleware):
     """
     The waffle.middleware.WaffleMiddleware class does not yet provide a way
diff --git a/kuma/settings/common.py b/kuma/settings/common.py
--- a/kuma/settings/common.py
+++ b/kuma/settings/common.py
@@ -423,7 +423,7 @@ def _get_locales():
 
 MIDDLEWARE = (
     'django.middleware.security.SecurityMiddleware',
-    'kuma.core.middleware.RestrictedWhiteNoiseMiddleware',
+    'whitenoise.middleware.WhiteNoiseMiddleware',
     # must come before LocaleMiddleware
     'redirect_urls.middleware.RedirectsMiddleware',
     'kuma.core.middleware.SetRemoteAddrFromForwardedFor',


Test patch:
diff --git a/kuma/core/tests/test_middleware.py b/kuma/core/tests/test_middleware.py
--- a/kuma/core/tests/test_middleware.py
+++ b/kuma/core/tests/test_middleware.py
@@ -1,4 +1,4 @@
-from unittest.mock import MagicMock, patch
+from unittest.mock import MagicMock
 
 import pytest
 from django.core.exceptions import MiddlewareNotUsed
@@ -8,10 +8,8 @@
 from ..middleware import (
     ForceAnonymousSessionMiddleware,
     RestrictedEndpointsMiddleware,
-    RestrictedWhiteNoiseMiddleware,
     SetRemoteAddrFromForwardedFor,
     WaffleWithCookieDomainMiddleware,
-    WhiteNoiseMiddleware,
 )
 
 
@@ -97,27 +95,6 @@ def test_restricted_endpoints_middleware_when_disabled(settings):
         RestrictedEndpointsMiddleware(lambda req: None)
 
 
-def test_restricted_whitenoise_middleware(rf, settings):
-    settings.ATTACHMENT_HOST = 'demos'
-    settings.ENABLE_RESTRICTIONS_BY_HOST = True
-    settings.ALLOWED_HOSTS.append('demos')
-
-    middleware = RestrictedWhiteNoiseMiddleware(lambda req: None)
-    sentinel = object()
-
-    with patch.object(WhiteNoiseMiddleware, 'process_request',
-                      return_value=sentinel):
-        request = rf.get('/foo', HTTP_HOST='demos')
-        assert middleware(request) is None
-
-        request = rf.get('/foo', HTTP_HOST='testserver')
-        assert middleware(request) is sentinel
-
-        settings.ENABLE_RESTRICTIONS_BY_HOST = False
-        request = rf.get('/foo', HTTP_HOST='demos')
-        assert middleware(request) is sentinel
-
-
 def test_waffle_cookie_domain_middleware(rf, settings):
     settings.WAFFLE_COOKIE = 'dwf_%s'
     settings.WAFFLE_COOKIE_DOMAIN = 'mdn.dev'