Repository: learningequality/kolibri

Issue:
Handle SuspiciousFileOperations in kolibri_whitenoise and return a 404
SuspiciousFileOperation: The joined path (/usr/local/lib/windows/win.ini-0.1.0.js) is located outside of the base path com...

Sentry Issue: [KOLIBRI-BACKEND-2D4](https://learningequality.sentry.io/issues/3912524995/?referrer=github_integration)

```
SuspiciousFileOperation: The joined path (/usr/local/lib/windows/win.ini-0.1.0.js) is located outside of the base path component (/usr/local/lib/python3.10/site-packages/kolibri/core/static)
(4 additional frame(s) were not displayed)
...
  File "django/contrib/staticfiles/finders.py", line 160, in find
    match = self.find_in_app(app, path)
  File "django/contrib/staticfiles/finders.py", line 174, in find_in_app
    if storage.exists(path):
  File "django/core/files/storage.py", line 392, in exists
    return os.path.exists(self.path(name))
  File "django/core/files/storage.py", line 405, in path
    return safe_join(self.location, name)
  File "django/utils/_os.py", line 76, in safe_join
    raise SuspiciousFileOperation(
```


Discussion:

Resolving patch:
diff --git a/kolibri/utils/kolibri_whitenoise.py b/kolibri/utils/kolibri_whitenoise.py
--- a/kolibri/utils/kolibri_whitenoise.py
+++ b/kolibri/utils/kolibri_whitenoise.py
@@ -6,6 +6,7 @@
 from wsgiref.headers import Headers
 
 from django.contrib.staticfiles import finders
+from django.core.exceptions import SuspiciousFileOperation
 from django.core.files.storage import FileSystemStorage
 from django.utils._os import safe_join
 from six.moves.urllib.parse import parse_qs
@@ -294,10 +295,13 @@ def find_and_cache_dynamic_file(self, url, remote_baseurl):
         return self.files.get(url)
 
     def get_dynamic_path(self, url):
-        if self.static_prefix is not None and url.startswith(self.static_prefix):
-            return finders.find(url[len(self.static_prefix) :])
-        if self.dynamic_check is not None and self.dynamic_check.match(url):
-            return self.dynamic_finder.find(url)
+        try:
+            if self.static_prefix is not None and url.startswith(self.static_prefix):
+                return finders.find(url[len(self.static_prefix) :])
+            if self.dynamic_check is not None and self.dynamic_check.match(url):
+                return self.dynamic_finder.find(url)
+        except SuspiciousFileOperation:
+            pass
 
     def candidate_paths_for_url(self, url):
         paths = super(DynamicWhiteNoise, self).candidate_paths_for_url(url)


Test patch:
diff --git a/kolibri/utils/tests/test_kolibri_whitenoise.py b/kolibri/utils/tests/test_kolibri_whitenoise.py
--- a/kolibri/utils/tests/test_kolibri_whitenoise.py
+++ b/kolibri/utils/tests/test_kolibri_whitenoise.py
@@ -5,6 +5,7 @@
 
 from kolibri.utils.kolibri_whitenoise import DynamicWhiteNoise
 from kolibri.utils.kolibri_whitenoise import FileFinder
+from kolibri.utils.kolibri_whitenoise import NOT_FOUND
 
 
 def test_file_finder():
@@ -86,3 +87,24 @@ def test_dynamic_whitenoise():
     os.remove(tempdir22tempfilepath)
     os.removedirs(tempdir11)
     os.removedirs(tempdir12)
+
+
+def test_dynamic_whitenoise_suspicious_file():
+    tempdir11 = tempfile.mkdtemp()
+    tempdir12 = tempfile.mkdtemp()
+    prefix1 = "/test"
+    dynamic_whitenoise = DynamicWhiteNoise(
+        MagicMock(),
+        dynamic_locations=[
+            (prefix1, tempdir11),
+            (prefix1, tempdir12),
+        ],
+    )
+    assert (
+        dynamic_whitenoise.find_and_cache_dynamic_file(
+            prefix1 + "/" + tempdir11 + "../../../leet_haxx0r.js", None
+        )
+        is not NOT_FOUND
+    )
+    os.removedirs(tempdir11)
+    os.removedirs(tempdir12)