CVE-2024-35184: Insufficient Information (NVD-CWE-noinfo)
value: Paperless-ngx is a document management system that transforms physical documents into a searchable online archive. Starting in version 2.5.0 and prior to version 2.8.6, remote user authentication allows API access even if API access is explicitly disabled. Version 2.8.6 contains a patchc for the issue.
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
Published: 2024-05-15T22:15Z
Repository: https://github.com/paperless-ngx/paperless-ngx
Language: Python
Files changed: src/paperless/auth.py, src/paperless/tests/test_remote_user.py
Commit date: 2024-05-15 20:18:50 +0000
Commit message: Security: disallow API remote-user auth if disabled (#6739)


Diff: diff --git a/src/paperless/auth.py b/src/paperless/auth.py
index ba9320b5d..6ca97d608 100644
--- a/src/paperless/auth.py
+++ b/src/paperless/auth.py
@@ -50,10 +50,21 @@ class HttpRemoteUserMiddleware(PersistentRemoteUserMiddleware):
     example by certain SSO applications.
     """
 
     header = settings.HTTP_REMOTE_USER_HEADER_NAME
 
+    def process_request(self, request: HttpRequest) -> None:
+        # If remote user auth is enabled only for the frontend, not the API,
+        # then we need dont want to authenticate the user for API requests.
+        if (
+            "/api/" in request.path
+            and "paperless.auth.PaperlessRemoteUserAuthentication"
+            not in settings.REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"]
+        ):
+            return
+        return super().process_request(request)
+
 
 class PaperlessRemoteUserAuthentication(authentication.RemoteUserAuthentication):
     """
     REMOTE_USER authentication for DRF which overrides the default header.
     """
diff --git a/src/paperless/tests/test_remote_user.py b/src/paperless/tests/test_remote_user.py
index c5d7a6db4..ebe1b3ff5 100644
--- a/src/paperless/tests/test_remote_user.py
+++ b/src/paperless/tests/test_remote_user.py
@@ -1,9 +1,10 @@
 import os
 from unittest import mock
 
 from django.contrib.auth.models import User
+from django.test import override_settings
 from rest_framework import status
 from rest_framework.test import APITestCase
 
 from documents.tests.utils import DirectoriesMixin
 from paperless.settings import _parse_remote_user_settings
@@ -86,10 +87,42 @@ class TestRemoteUser(DirectoriesMixin, APITestCase):
                 },
             )
 
             self.assertEqual(response.status_code, status.HTTP_200_OK)
 
+    @override_settings(
+        REST_FRAMEWORK={
+            "DEFAULT_AUTHENTICATION_CLASSES": [
+                "rest_framework.authentication.BasicAuthentication",
+                "rest_framework.authentication.TokenAuthentication",
+                "rest_framework.authentication.SessionAuthentication",
+            ],
+        },
+    )
+    def test_remote_user_api_disabled(self):
+        """
+        GIVEN:
+            - Configured user
+            - Remote user auth enabled for frontend but disabled for the API
+            - Note that REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'] is set in settings.py in production
+        WHEN:
+            - API call is made to get documents
+        THEN:
+            - Call fails
+        """
+        response = self.client.get(
+            "/api/documents/",
+            headers={
+                "Remote-User": self.user.username,
+            },
+        )
+
+        self.assertIn(
+            response.status_code,
+            [status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN],
+        )
+
     def test_remote_user_header_setting(self):
         """
         GIVEN:
             - Remote user header name is set
         WHEN:

Fixed code:     def process_request(self, request: HttpRequest) -> None:
        # If remote user auth is enabled only for the frontend, not the API,
        # then we need dont want to authenticate the user for API requests.
        if (
            "/api/" in request.path
            and "paperless.auth.PaperlessRemoteUserAuthentication"
            not in settings.REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"]
        ):
            return
        return super().process_request(request)

from django.test import override_settings
    @override_settings(
        REST_FRAMEWORK={
            "DEFAULT_AUTHENTICATION_CLASSES": [
                "rest_framework.authentication.BasicAuthentication",
                "rest_framework.authentication.TokenAuthentication",
                "rest_framework.authentication.SessionAuthentication",
            ],
        },
    )
    def test_remote_user_api_disabled(self):
        """
        GIVEN:
            - Configured user
            - Remote user auth enabled for frontend but disabled for the API
            - Note that REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'] is set in settings.py in production
        WHEN:
            - API call is made to get documents
        THEN:
            - Call fails
        """
        response = self.client.get(
            "/api/documents/",
            headers={
                "Remote-User": self.user.username,
            },
        )

        self.assertIn(
            response.status_code,
            [status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN],
        )