Repository: learningequality/kolibri

Issue:
AssertionError: 'xxxx' is not a valid content storage filename
Sentry Issue: [KOLIBRI-BACKEND-6](https://sentry.io/learningequality/kolibri-backend/issues/877445818/?referrer=github_integration)

```
AssertionError: 'fonts' is not a valid content storage filename
(7 additional frame(s) were not displayed)
...
  File "django/views/decorators/clickjacking.py", line 58, in wrapped_view
    resp = view_func(*args, **kwargs)
  File "django/utils/decorators.py", line 67, in _wrapper
    return bound_func(*args, **kwargs)
  File "django/views/decorators/http.py", line 88, in inner
    res_etag = etag_func(request, *args, **kwargs) if etag_func else None
  File "kolibri/core/content/views.py", line 90, in calculate_zip_content_etag
    zipped_path = get_content_storage_file_path(zipped_filename)
  File "kolibri/core/content/utils/paths.py", line 73, in get_content_storage_file_path
    assert VALID_STORAGE_FILENAME.match(filename), "'{}' is not a valid content storage filename".format(filename)
```


Discussion:

Resolving patch:
diff --git a/kolibri/core/content/views.py b/kolibri/core/content/views.py
--- a/kolibri/core/content/views.py
+++ b/kolibri/core/content/views.py
@@ -87,7 +87,10 @@ def _add_content_security_policy_header(request, response):
 
 
 def calculate_zip_content_etag(request, zipped_filename, embedded_filepath):
-    zipped_path = get_content_storage_file_path(zipped_filename)
+    try:
+        zipped_path = get_content_storage_file_path(zipped_filename)
+    except AssertionError:
+        return None
 
     # if no path, or a directory, is being referenced, look for an index.html file
     if not embedded_filepath or embedded_filepath.endswith("/"):
@@ -101,6 +104,14 @@ def calculate_zip_content_etag(request, zipped_filename, embedded_filepath):
     return hashlib.md5((zipped_filename + embedded_filepath).encode('utf-8')).hexdigest()
 
 
+def get_path_or_404(zipped_filename):
+    try:
+        # calculate the local file path to the zip file
+        return get_content_storage_file_path(zipped_filename)
+    except AssertionError:
+        raise Http404('"%(filename)s" is not a valid zip file' % {'filename': zipped_filename})
+
+
 class ZipContentView(View):
 
     @xframe_options_exempt
@@ -120,9 +131,7 @@ def get(self, request, zipped_filename, embedded_filepath):
         """
         Handles GET requests and serves a static file from within the zip file.
         """
-
-        # calculate the local file path to the zip file
-        zipped_path = get_content_storage_file_path(zipped_filename)
+        zipped_path = get_path_or_404(zipped_filename)
 
         # file size
         file_size = 0


Test patch:
diff --git a/kolibri/core/content/test/test_zipcontent.py b/kolibri/core/content/test/test_zipcontent.py
--- a/kolibri/core/content/test/test_zipcontent.py
+++ b/kolibri/core/content/test/test_zipcontent.py
@@ -90,6 +90,10 @@ def test_zip_file_nonexistent_internal_file_access(self):
         response = self.client.get(self.zip_file_base_url + "qqq" + self.test_name_1)
         self.assertEqual(response.status_code, 404)
 
+    def test_non_allowed_file_internal_file_access(self):
+        response = self.client.get(self.zip_file_base_url.replace('zip', 'png') + self.test_name_1)
+        self.assertEqual(response.status_code, 404)
+
     def test_not_modified_response_when_if_modified_since_header_set(self):
         caching_client = Client(HTTP_IF_MODIFIED_SINCE="Sat, 10-Sep-2016 19:14:07 GMT")
         response = caching_client.get(self.zip_file_base_url + self.test_name_1)